Compare commits

...

190 Commits

Author SHA1 Message Date
Pang
fe5f56e98b build 2024-04-09 23:35:22 +08:00
Pang
40ef700e5a perf: 优化使用默认浏览器打开规则 2024-04-09 20:46:56 +08:00
Pang
8661c28d10 build 2024-04-09 19:34:19 +08:00
Pang
9edddc461d perf: 优化聊天图片上传 2024-04-09 09:05:36 +08:00
kuaifan
2fbb640bc8 build 2024-04-08 17:00:39 +08:00
kuaifan
a03050bc7b perf: 临时帐号别名 2024-04-08 16:53:24 +08:00
kuaifan
654a90626e perf: tab icon load error 2024-04-08 14:06:45 +08:00
Pang
9acafed459 perf: 优化会议 2024-04-08 09:37:46 +08:00
Pang
b7dcb543f6 perf: 创建会议不需要加入机器人 2024-04-08 08:58:16 +08:00
Pang
e2768f7f20 perf: 暗黑模式下窗口背景色兼容问题 2024-04-08 08:31:51 +08:00
Pang
cda2d0da27 perf: 优化网络检查 2024-04-08 08:13:30 +08:00
Pang
c61815db3a perf: 客户端会议优化 2024-04-08 07:51:57 +08:00
Pang
9390965a0c no message 2024-04-06 11:58:45 +08:00
Pang
0688feefb1 build 2024-04-04 10:33:51 +08:00
Pang
93c8d86caf perf: 优化会议室 2024-04-04 10:31:12 +08:00
Pang
540bff89cf no message 2024-04-04 08:42:41 +08:00
Pang
41c09b3838 perf: 优化会议室 2024-04-04 07:40:57 +08:00
Pang
0a26361724 no message 2024-04-04 07:40:38 +08:00
kuaifan
ee9ad65e18 build 2024-04-03 10:32:00 +08:00
kuaifan
db6b571cfb fix: 部分情况出现注册失败 2024-04-03 10:14:41 +08:00
Pang
bfe359c440 perf: 优化数据读取机制 2024-04-03 07:53:44 +08:00
Pang
ee8f67793a fix: 最小化阅读窗口新建窗口不自动激活 2024-04-03 07:13:35 +08:00
Pang
629fe79c61 fix: 独立窗口不更新消息 2024-04-03 07:13:35 +08:00
Pang
9ae278d622 perf: 优化缓存规则 2024-04-03 07:13:35 +08:00
Pang
3417d68609 perf: 优化完成待办数据推送 2024-04-03 07:13:35 +08:00
weifs
f757749282 perf: 评论审批图片和投票深色按钮 2024-03-26 17:43:08 +08:00
kuaifan
ea40e95cae build 2024-03-26 14:19:02 +08:00
kuaifan
eb066f52fe perf: 优化任务日志内容 2024-03-26 13:52:24 +08:00
kuaifan
b7007135cb perf: 查看版本免请求接口 2024-03-26 13:28:00 +08:00
kuaifan
a7bd403b2c perf: 添加任务时选择任务位置内容溢出 2024-03-26 13:22:25 +08:00
Pang
59c7b148dd perf: 消息支持style 2024-03-26 10:17:49 +08:00
Pang
c67f52e960 perf: 回复消息列表隐藏顶部loading 2024-03-26 08:56:20 +08:00
Pang
f311625060 fix: 修改回复、转发消息后引用的部分消失 2024-03-26 08:51:40 +08:00
kuaifan
d3c08f8d90 perf: 支持FCM推送 2024-03-21 00:37:38 +09:00
kuaifan
2bc655d7ef perf: 设置华为推送自分类 2024-03-20 15:52:48 +09:00
kuaifan
d2b8d0372e perf(client): 优化添加任务可见性点击效果 2024-03-20 13:44:22 +09:00
kuaifan
40b637b16e build 2024-03-20 02:53:37 +09:00
kuaifan
6e68f399b4 perf: 优化数据结构 2024-03-20 02:49:44 +09:00
kuaifan
0be6c70e92 Merge commit '6c2d8fc16313234bbacb4ad4d7f8637b71025a26' into pro 2024-03-20 02:03:26 +09:00
weifs
6c2d8fc163 perf: 接龙优化为清空内容默认删除 2024-03-19 18:41:41 +08:00
kuaifan
a8193b8feb build 2024-03-19 16:32:39 +09:00
kuaifan
34159caf22 Merge commit 'd12c0c42072452de4c99ef55c5915edb108dd2ef' into pro 2024-03-19 16:04:26 +09:00
kuaifan
c75f406459 no message 2024-03-19 15:57:24 +09:00
kuaifan
99dca06d44 perf: 支持取消发送中的消息 2024-03-19 15:57:07 +09:00
weifs
d12c0c4207 perf: 1. 强化接龙接口本地时间戳问题 2. 接龙消息点展开按钮后做缓存处理 2024-03-19 14:38:15 +08:00
kuaifan
915a5ed7d5 fix: 关闭文件后无法再次打开 2024-03-19 12:07:54 +09:00
kuaifan
7bfc43c85f no message 2024-03-19 04:06:45 +09:00
kuaifan
77ea022ddf build 2024-03-19 03:23:09 +09:00
kuaifan
93578f93f4 Merge commit 'cbbd50a2e320ca0427474fb2921f2b93a5ad2c14' into pro 2024-03-19 03:17:19 +09:00
kuaifan
f129615ebe no message 2024-03-19 03:15:38 +09:00
kuaifan
0e5b44baad perf: 自动识别md格式发送 2024-03-19 03:15:19 +09:00
kuaifan
3596475790 fix: 消息太长导致菜单无法正常显示 2024-03-19 03:14:09 +09:00
kuaifan
6218521dea fix: 项目数量不正确的情况 2024-03-19 02:29:06 +09:00
kuaifan
65db8b5703 fix: 部分未读和待办信息不显示的情况 2024-03-19 02:21:48 +09:00
kuaifan
f5ff9a3648 perf: 优化回复、转发消息数据结构 2024-03-18 19:44:20 +09:00
weifs
cbbd50a2e3 fix: 审批中心修复loadIng效果 2024-03-18 14:12:19 +08:00
kuaifan
b04647e65a build 2024-03-17 17:50:27 +09:00
kuaifan
d34d94faa6 perf: 优化iOS端数据读取失败的情况 2024-03-17 17:21:29 +09:00
kuaifan
4038d9560f perf: 回复消息时自动@提及 2024-03-17 17:00:05 +09:00
kuaifan
006fc43498 perf: 优化会话数据结构 2024-03-17 16:23:57 +09:00
kuaifan
47c9b2e1b0 build 2024-03-15 12:51:10 +09:00
kuaifan
dc3e5f0a59 perf: 聊天文件发送进度 2024-03-15 12:48:33 +09:00
kuaifan
01bda83fcd build 2024-03-15 11:24:46 +09:00
kuaifan
9ecb9c68fb perf: 拨打电话确认提示 2024-03-15 11:16:20 +09:00
kuaifan
4612d5180a no message 2024-03-15 11:15:58 +09:00
kuaifan
cfb653796c perf: 优化预加载文件 2024-03-15 10:58:19 +09:00
kuaifan
d00cd5cb26 fix: 子窗口出现重新登录的情况 2024-03-15 10:19:24 +09:00
kuaifan
285a62c87e no message 2024-03-15 02:15:03 +09:00
kuaifan
bcb0c6bc77 build 2024-03-14 13:48:38 +09:00
kuaifan
d1ab2d98eb no message 2024-03-14 13:45:01 +09:00
kuaifan
c3d5328154 perf: 优化接口时间 2024-03-14 13:44:46 +09:00
kuaifan
fc30588014 build 2024-03-14 08:15:46 +09:00
kuaifan
65b02001b2 no message 2024-03-14 08:01:28 +09:00
kuaifan
cd011a172f perf: 优化审批对话按钮配色 2024-03-14 08:01:17 +09:00
kuaifan
bf913d9eff fix: 回复消息点击到原文无效 2024-03-14 07:26:19 +09:00
kuaifan
c2dd15fca1 no message 2024-03-14 02:55:42 +09:00
kuaifan
b267863b58 build 2024-03-13 14:46:32 +09:00
kuaifan
d189fb100a Merge commit 'c6568969c7b2d538d27cb4ca0ee412d4dbdceb56' into pro 2024-03-13 14:39:39 +09:00
kuaifan
dc6c5bef26 no message 2024-03-13 14:38:49 +09:00
kuaifan
7208d51644 perf: 优化文件功能按钮 2024-03-13 14:38:38 +09:00
kuaifan
16359a968d perf: 文件上传支持覆盖上传 2024-03-13 14:25:15 +09:00
kuaifan
d553f77533 perf: 优化app等比显示 2024-03-13 11:34:27 +09:00
kuaifan
bc25f5dfdf perf: 优化发送文件预览 2024-03-13 10:58:46 +09:00
kuaifan
d40028340c perf: 消息发送中禁止右键菜单 2024-03-13 08:03:58 +09:00
kuaifan
4194d1cddd perf: 部分搜索框图标抖动 2024-03-13 07:57:41 +09:00
kuaifan
1fdd532133 perf: 优化复制功能 2024-03-13 07:49:12 +09:00
kuaifan
71c62a3772 fix: 客户端无法打开excel文件 2024-03-13 07:32:16 +09:00
kuaifan
9be6cd5148 perf: 优化pdf文件预览 2024-03-13 07:14:27 +09:00
wfs
c6568969c7 style: 调整代码格式 2024-03-13 00:26:13 +08:00
wfs
f5b1a6ab05 fix: 修复投票实名逻辑 2024-03-13 00:25:10 +08:00
wfs
5efe659cf5 perf: 优化投票接口,加上事务锁 2024-03-13 00:17:23 +08:00
wfs
b254fd5ce2 perf: 优化接龙接口,加上事务锁 2024-03-12 23:49:05 +08:00
kuaifan
631a0ffff4 no message 2024-03-13 00:42:15 +09:00
kuaifan
8b11e9a19e perf: 优化转发消息样式 2024-03-13 00:42:06 +09:00
weifs
f6b006b000 feat: 升级okr 2024-03-11 18:07:37 +08:00
weifs
3a26f420b8 perf: 接龙接口-强化排序 2024-03-11 17:25:40 +08:00
weifs
0919e415ec perf: 审批按钮色微调 2024-03-11 16:57:45 +08:00
weifs
030a07698d perf: 统一审批中心的按钮色 2024-03-11 16:36:12 +08:00
weifs
a7f2582df7 perf: 转发会议亮色皮肤问题,转发文件宽度铺满 2024-03-11 10:39:53 +08:00
Pang
5f0a0e0371 no message 2024-03-10 22:13:21 +08:00
Pang
28717fd0c7 perf: 优化app数据交互 2024-03-10 14:44:55 +08:00
Pang
7014ea176a build 2024-03-10 12:12:30 +08:00
Pang
b4f2da66be fix: 修复搜索偶尔无效的情况 2024-03-10 11:03:34 +08:00
Pang
b53462cf6e build 2024-03-10 00:39:01 +08:00
Pang
8b40364722 no message 2024-03-10 00:36:07 +08:00
Pang
6ee1824410 perf: 优化文件预览 2024-03-09 17:38:33 +08:00
Pang
f63c2da37a no message 2024-03-09 10:44:36 +08:00
Pang
9be0642ba5 perf: 滑动列表自动隐藏键盘 2024-03-09 10:35:44 +08:00
Pang
55a922c7b3 perf: 优化时间格式 2024-03-09 10:16:09 +08:00
kuaifan
50893929d6 perf: 适配nodejs 20 2024-03-08 17:04:56 +08:00
weifs
03c94e791a feat: 升级okr 2024-03-08 11:03:16 +08:00
weifs
96bb554813 feat: 升级okr 2024-03-08 10:48:33 +08:00
Pang
1bc77de144 build 2024-03-07 23:13:44 +08:00
Pang
aa07c78fc8 perf: 修改消息换行优化 2024-03-07 23:13:24 +08:00
kuaifan
52dda88d40 build 2024-03-07 22:47:04 +08:00
kuaifan
c555b309bd feat: 新增不显示会话功能 2024-03-07 21:05:46 +08:00
kuaifan
0a51225762 Merge commit '3d725ddeef1f86eb02ff2b33435c23bc5e6da8de' into pro 2024-03-07 19:26:49 +08:00
kuaifan
fab49b1dda no message 2024-03-07 19:22:41 +08:00
kuaifan
57e422f2d3 perf: 优化预加载资源 2024-03-07 15:54:55 +08:00
kuaifan
50a1a3147e perf: 优化pdf文件预览 2024-03-07 15:54:26 +08:00
kuaifan
277115a30f perf: 优化签到消息 2024-03-07 15:53:31 +08:00
kuaifan
7464de3adc fix: 部分手机出现非正常滚动到底部的情况 2024-03-07 15:52:47 +08:00
HEXIANG
3d725ddeef fix:dootask官网标题 2024-03-07 15:17:25 +08:00
Pang
38d8f289e4 no message 2024-03-07 08:27:47 +08:00
Pang
edfd6e6de2 perf: 优化登录 2024-03-07 07:17:24 +08:00
Pang
a68ab6512e Merge commit '8383b88a448501cfdb32b79b6e2ccafedadf5e97' into pro 2024-03-06 22:03:34 +08:00
HEXIANG
8383b88a44 fix:dootask官网调整 2024-03-06 18:01:50 +08:00
kuaifan
27ff24f44e perf: 优化安装脚本 2024-03-06 17:18:12 +08:00
weifs
b111ecb227 fix: 修复导出任务统计没有按创建时间来的bug 2024-03-06 14:00:13 +08:00
kuaifan
ac17952cd3 perf: 优化消息时间格式 2024-03-06 12:01:18 +08:00
kuaifan
e24978fdd7 perf: 优化app功能 2024-03-06 11:47:05 +08:00
weifs
a4d7579e3f feat: 升级okr 2024-03-06 10:23:47 +08:00
Pang
52171b794a build 2024-03-06 02:44:16 +08:00
Pang
3c33f02e9d Merge commit '0968c43f61e0183aaf47e38a482d037bc33fc434' into pro 2024-03-06 01:08:59 +08:00
Pang
0a8823c40b no message 2024-03-06 01:05:30 +08:00
Pang
a3f7e71638 translate 2024-03-06 01:01:29 +08:00
Pang
7ebf4fb9ce perf: 默认映射443端口 2024-03-06 01:01:12 +08:00
kuaifan
c96bad3cdf perf: 优化子窗口 2024-03-06 00:30:30 +08:00
weifs
0968c43f61 feat: 发起投票功能添加缓存记录选中效果 2024-03-05 17:50:13 +08:00
weifs
ae147c76ff perf: 优化发布接口 删除目录的逻辑 2024-03-05 15:22:40 +08:00
weifs
0e916a2804 feat: 导出的签到数据和审批数据换成xlsx,因老版本的xls会出现兼容性问题 2024-03-05 15:19:12 +08:00
weifs
494565e131 perf: 优化发布接口 删除目录的逻辑 2024-03-04 09:51:35 +08:00
kuaifan
c4430e1a6c build 2024-03-02 14:56:12 +08:00
kuaifan
26adfa11bf Merge commit '6230bf94c5abfa7e67c6226e0b0dc088d80ff055' into pro 2024-03-02 14:39:47 +08:00
kuaifan
69ec57669e perf: 更新说明文档 2024-03-02 14:11:52 +08:00
kuaifan
3556133585 perf: 优化客户端 2024-03-02 14:11:33 +08:00
kuaifan
a65181757d perf: 默认关闭端到端加密传输 2024-03-02 13:21:01 +08:00
kuaifan
42d39a830e fix: 修改消息导致最后消息改变 2024-03-02 13:07:27 +08:00
kuaifan
2e70c9617c fix: 显示无关系的子任务、指定成员可见消息推送 2024-03-02 12:52:16 +08:00
zzw
6230bf94c5 fix:调整gemini机器人设置参数 2024-03-01 17:47:41 +08:00
kuaifan
47832ececb Merge commit '76570e2f1b72de50cb54c36229447eea69c59940' into pro 2024-03-01 16:02:36 +08:00
kuaifan
60e6003485 build 2024-02-29 19:47:45 +08:00
kuaifan
9133f289b4 perf: 优化ai机器人 2024-02-29 19:39:05 +08:00
gwok
76570e2f1b fix:修复左侧滚动菜单不跟随右侧正文自动上滑 2024-02-29 18:25:26 +08:00
weifs
c9234a4b49 feat: 升级okr 2024-02-29 16:22:25 +08:00
weifs
c1361fadda perf: okr和审批优化 2024-02-29 15:26:45 +08:00
zzw
ec7af94f71 fix:调整发送参数逻辑 2024-02-29 11:39:30 +08:00
kuaifan
81690d6ce9 build 2024-02-28 15:33:33 +08:00
kuaifan
236b57864b Merge commit 'e3ce3bcfbe1d251c791fddbf2627b35669bdd2df' into pro 2024-02-28 15:21:43 +08:00
kuaifan
22259ec34d build 2024-02-28 15:15:25 +08:00
kuaifan
5a4700753a perf: 优化pdf在线预览 2024-02-28 15:04:56 +08:00
kuaifan
cc862741dc fix: 推送标题存在换行时不显示 2024-02-28 15:04:31 +08:00
Pang
779b32e8ad feat: 优化内置浏览器 2024-02-28 15:03:56 +08:00
zzw
e3ce3bcfbe feat:gemini机器人添加代理参数 2024-02-28 14:37:18 +08:00
zzw
673053f181 feat:集成geminiAI 2024-02-27 18:23:07 +08:00
Pang
b6eb77ae63 no message 2024-02-24 09:18:12 +08:00
kuaifan
0e63255a7f build 2024-02-23 22:11:43 +08:00
kuaifan
f42408a363 no message 2024-02-23 22:11:43 +08:00
kuaifan
897fc51ce3 build 2024-02-23 22:11:43 +08:00
Pang
6848b126c5 perf: 优化客户端打开服务器链接 2024-02-23 22:11:43 +08:00
Pang
0ed9afd1bd no message 2024-02-23 22:11:43 +08:00
weifs
26cca8298f fix: 修复下载文件大小为0时报错 2024-02-23 13:53:03 +08:00
weifs
58407af2ba fix: 更改其他版本的链接 2024-02-23 09:40:00 +08:00
spylecym
3a0473a74f fix: 修复价格页面样式 2024-02-23 09:13:00 +08:00
weifs
6e5124fe22 fix: 发布接口,调整缓存时间为两小时 2024-02-22 19:19:50 +08:00
weifs
02bd022c62 feat: 发布接口只保留最近两个版本 2024-02-22 19:18:04 +08:00
weifs
f2538884ea perf: 签到设置,有些客户服务器安全体系会拦截 curl -sSL 关键字,优化为cmd不传值 2024-02-22 11:07:54 +08:00
weifs
8d121d4056 perf: 签到设置,有些客户服务器安全体系会拦截 curl -sSL 关键字,优化为base64返回 2024-02-22 11:02:32 +08:00
weifs
96438604ee perf: 升级okr 2024-02-21 17:30:59 +08:00
ganzizi
63ccd675d0 fix: 修复okr定时处理信息不发送 2024-02-21 16:05:32 +08:00
weifs
2c08145c40 fix: 项目已归档,任务面板也没有这三个任务,但是每次新增报告,都会弹任务出来 2024-02-21 14:42:30 +08:00
spylecym
12effb5738 fix: 修改下载页面按钮布局样式 2024-02-20 15:52:32 +08:00
weifs
91bfb989be feat: 官网添加其他版本的按钮 2024-02-20 15:05:45 +08:00
weifs
192de79fea feat: 统一表为utf8mb4_unicode_ci 2024-02-20 15:00:38 +08:00
Pang
82063f1b21 build 2024-02-06 07:27:43 +08:00
kuaifan
02b263439b perf: 升级okr容器 2024-02-05 21:26:53 +08:00
kuaifan
7efaf3bb32 perf: 新增禁止私聊、群聊功能 2024-02-05 21:24:45 +08:00
kuaifan
7dd5b082cf perf: 更换笑话和鸡汤接口 2024-02-05 13:33:42 +08:00
Pang
6320eaa3ac build 2024-01-25 21:52:57 +08:00
Pang
85b88b6b61 no message 2024-01-21 20:20:02 +08:00
320 changed files with 7750 additions and 2840 deletions

View File

@@ -10,6 +10,7 @@ APP_URL=http://localhost
APP_ID=
APP_IPPR=
APP_PORT=2222
APP_SSL_PORT=
APP_DEV_PORT=
LOG_CHANNEL=stack
@@ -56,9 +57,6 @@ 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}"

View File

@@ -1,4 +1,4 @@
name: Publish Desktop
name: Publish Mac
on:
push:
@@ -6,7 +6,7 @@ on:
- 'v*'
jobs:
build:
Build:
runs-on: macos-latest
environment: build
@@ -15,10 +15,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Use Node.js 16.x
- name: Use Node.js 20.x
uses: actions/setup-node@v1
with:
node-version: 16.x
node-version: 20.x
- name: Build
env:
@@ -29,5 +29,6 @@ jobs:
DP_KEY: ${{ secrets.DP_KEY }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
GH_REPOSITORY: ${{ github.repository }}
run: ./cmd electron all
run: |
./cmd electron mac

View File

@@ -0,0 +1,30 @@
name: Publish Win
on:
push:
tags:
- 'v*'
jobs:
Build:
runs-on: windows-latest
environment: build
if: startsWith(github.event.ref, 'refs/tags/v')
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Use Node.js 20.x
uses: actions/setup-node@v1
with:
node-version: 20.x
- name: Build
env:
DP_KEY: ${{ secrets.DP_KEY }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
GH_REPOSITORY: ${{ github.repository }}
shell: bash
run: |
./cmd electron win

27
.prefetch Normal file
View File

@@ -0,0 +1,27 @@
office/web-apps/apps/api/documents/api.js?hash={version}
office/7.5.1-23/sdkjs/cell/css/main.css
office/7.5.1-23/web-apps/apps/spreadsheeteditor/main/resources/css/app.css
office/7.5.1-23/web-apps/vendor/requirejs/require.js
office/7.5.1-23/web-apps/apps/spreadsheeteditor/main/app.js
office/7.5.1-23/sdkjs/common/AllFonts.js
office/7.5.1-23/web-apps/vendor/xregexp/xregexp-all-min.js
office/7.5.1-23/web-apps/vendor/socketio/socket.io.min.js
office/7.5.1-23/sdkjs/cell/sdk-all-min.js
office/7.5.1-23/sdkjs/cell/sdk-all.js
office/7.5.1-23/sdkjs/common/libfont/engine/fonts.js
office/7.5.1-23/sdkjs/common/Charts/ChartStyles.js
office/7.5.1-23/sdkjs/common/libfont/engine/fonts.wasm
office/7.5.1-23/web-apps/apps/presentationeditor/main/resources/css/app.css
office/7.5.1-23/web-apps/apps/presentationeditor/main/app.js
office/7.5.1-23/sdkjs/slide/sdk-all-min.js
office/7.5.1-23/sdkjs/slide/sdk-all.js
office/7.5.1-23/sdkjs/slide/themes//themes.js
office/7.5.1-23/web-apps/apps/documenteditor/main/resources/css/app.css
office/7.5.1-23/web-apps/apps/documenteditor/main/app.js
office/7.5.1-23/sdkjs/word/sdk-all-min.js
office/7.5.1-23/sdkjs/word/sdk-all.js
drawio/webapp/js/app.min.js
drawio/webapp/js/stencils.min.js
drawio/webapp/js/extensions.min.js
drawio/webapp/js/shapes-14-6-5.min.js
drawio/webapp/math/es5/output/svg/fonts/tex.js

View File

@@ -2,6 +2,254 @@
All notable changes to this project will be documented in this file.
## [0.36.60]
### Performance
- 优化使用默认浏览器打开规则
- 优化聊天图片上传
- 临时帐号别名
- Tab icon load error
- 优化会议
- 创建会议不需要加入机器人
- 暗黑模式下窗口背景色兼容问题
- 优化网络检查
- 客户端会议优化
## [0.36.49]
### Performance
- 优化会议室
## [0.36.44]
### Bug Fixes
- 部分情况出现注册失败
- 最小化阅读窗口新建窗口不自动激活
- 独立窗口不更新消息
### Performance
- 优化数据读取机制
- 优化缓存规则
- 优化完成待办数据推送
- 评论审批图片和投票深色按钮
## [0.36.36]
### Bug Fixes
- 修改回复、转发消息后引用的部分消失
### Performance
- 优化任务日志内容
- 查看版本免请求接口
- 添加任务时选择任务位置内容溢出
- 消息支持style
- 回复消息列表隐藏顶部loading
- 支持FCM推送
- 设置华为推送自分类
- 优化添加任务可见性点击效果
## [0.36.26]
### Bug Fixes
- 关闭文件后无法再次打开
### Performance
- 优化数据结构
- 接龙优化为清空内容默认删除
- 支持取消发送中的消息
- 1. 强化接龙接口本地时间戳问题 2. 接龙消息点展开按钮后做缓存处理
## [0.36.15]
### Bug Fixes
- 消息太长导致菜单无法正常显示
- 项目数量不正确的情况
- 部分未读和待办信息不显示的情况
- 审批中心修复loadIng效果
### Performance
- 自动识别md格式发送
- 优化回复、转发消息数据结构
- 优化iOS端数据读取失败的情况
- 回复消息时自动@提及
- 优化会话数据结构
## [0.35.90]
### Bug Fixes
- 子窗口出现重新登录的情况
### Performance
- 聊天文件发送进度
- 拨打电话确认提示
- 优化预加载文件
## [0.35.84]
### Bug Fixes
- 回复消息点击到原文无效
### Performance
- 优化接口时间
- 优化审批对话按钮配色
## [0.35.76]
### Bug Fixes
- 客户端无法打开excel文件
- 修复投票实名逻辑
### Features
- 升级okr
### Performance
- 优化文件功能按钮
- 文件上传支持覆盖上传
- 优化app等比显示
- 优化发送文件预览
- 消息发送中禁止右键菜单
- 部分搜索框图标抖动
- 优化复制功能
- 优化pdf文件预览
- 优化投票接口,加上事务锁
- 优化接龙接口,加上事务锁
- 优化转发消息样式
- 接龙接口-强化排序
- 审批按钮色微调
- 统一审批中心的按钮色
- 转发会议亮色皮肤问题,转发文件宽度铺满
- 优化app数据交互
### Styling
- 调整代码格式
## [0.35.48]
### Bug Fixes
- 修复搜索偶尔无效的情况
### Features
- 升级okr
### Performance
- 优化文件预览
- 滑动列表自动隐藏键盘
- 优化时间格式
- 适配nodejs 20
## [0.35.40]
### Bug Fixes
- 部分手机出现非正常滚动到底部的情况
- Dootask官网标题
- Dootask官网调整
- 修复导出任务统计没有按创建时间来的bug
### Features
- 新增不显示会话功能
- 升级okr
### Performance
- 修改消息换行优化
- 优化预加载资源
- 优化pdf文件预览
- 优化签到消息
- 优化登录
- 优化安装脚本
- 优化消息时间格式
- 优化app功能
## [0.35.20]
### Features
- 发起投票功能添加缓存记录选中效果
- 导出的签到数据和审批数据换成xlsx因老版本的xls会出现兼容性问题
### Performance
- 默认映射443端口
- 优化子窗口
- 优化发布接口 删除目录的逻辑
## [0.35.10]
### Bug Fixes
- 修改消息导致最后消息改变
- 显示无关系的子任务、指定成员可见消息推送
- 调整gemini机器人设置参数
### Performance
- 更新说明文档
- 优化客户端
- 默认关闭端到端加密传输
## [0.34.95]
### Bug Fixes
- 调整发送参数逻辑
- 推送标题存在换行时不显示
- 修复下载文件大小为0时报错
- 更改其他版本的链接
- 修复价格页面样式
- 发布接口,调整缓存时间为两小时
- 修复okr定时处理信息不发送
- 项目已归档,任务面板也没有这三个任务,但是每次新增报告,都会弹任务出来
- 修改下载页面按钮布局样式
### Features
- 升级okr
- 优化内置浏览器
- Gemini机器人添加代理参数
- 发布接口只保留最近两个版本
- 官网添加其他版本的按钮
- 统一表为utf8mb4_unicode_ci
### Performance
- 优化ai机器人
- Okr和审批优化
- 优化pdf在线预览
- 优化客户端打开服务器链接
- 签到设置,有些客户服务器安全体系会拦截 curl -sSL 关键字优化为cmd不传值
- 签到设置,有些客户服务器安全体系会拦截 curl -sSL 关键字优化为base64返回
## [0.34.66]
### Performance
- 升级okr容器
- 新增禁止私聊、群聊功能
- 更换笑话和鸡汤接口
## [0.34.59]
### Bug Fixes
@@ -10,6 +258,7 @@ All notable changes to this project will be documented in this file.
### Performance
- 修复一些问题
- IOS打开键盘时看不见通知的情况
- 优化系统参数
- 优化菜单显示位置

View File

@@ -2,7 +2,7 @@
English | **[中文文档](./README_CN.md)**
- [Screenshot Preview](README_PREVIEW.md)
- [Screenshot preview](./README_PREVIEW.md)
- [Demo site](http://www.dootask.com/)
**QQ Group**
@@ -12,8 +12,9 @@ Group No.: `546574618`
## Setup
- `Docker v20.10+` & `Docker Compose v2.0+` must be installed
- System: `Centos/Debian/Ubuntu/macOS`
- System: `Centos/Debian/Ubuntu/macOS/Windows`
- Hardware suggestion: 2 cores and above 4G memory
- Special note: Windows users please use `git bash` or `cmder` to run the command
### Deployment (Pro Edition)
@@ -28,7 +29,7 @@ git clone -b pro --depth=1 https://gitee.com/aipaw/dootask.git
# 2、Enter directory
cd dootask
# 3、InstallationCustom port installation: ./cmd install --port 2222
# 3、InstallationCustom port installation, as: ./cmd install --port 80
./cmd install
```
@@ -42,7 +43,8 @@ cd dootask
### Change port
```bash
./cmd port 2222
# This method only replaces the HTTP port. To replace the HTTPS port, please read the SSL configuration below
./cmd port 80
```
### Change App Url
@@ -66,11 +68,13 @@ cd dootask
### Development compilation
- `NodeJs 20+` must be installed
```bash
# Development mode, Mac OS only
# Development
./cmd dev
# Production projects, macOS only
# Production (This is web client. For App/PC/Mac clients, Please read README-CLIENT.md)
./cmd prod
```
@@ -84,11 +88,19 @@ cd dootask
./cmd redis "your command" # To run a redis command
./cmd composer "your command" # To run a composer command
./cmd supervisorctl "your command" # To run a supervisorctl command
./cmd test "your command" # To run a phpunit command
./cmd mysql "your command" # To run a mysql command (backup: Backup database, recovery: Restore database)
```
### NGINX PROXY SSL
### SSL configuration
#### Method 1: Automatic configuration
```bash
# Running commands in a project
./cmd https
```
#### Or Method 2: Nginx Agent Configuration
```bash
# 1、Nginx config add
@@ -96,8 +108,8 @@ proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 2、Running commands in a project
./cmd https
# 2、Running commands in a project (If you unconfigure the NGINX agent, run: ./cmd https close)
./cmd https agent
```
## Upgrade

View File

@@ -3,19 +3,23 @@
## 1、App客户端
#### 1.1、说明
目录 `resources/mobile`,使用`eeui.app`框架遵从eeui的开发文档进行打包开发app
#### 1.2、编译App
1. 在项目目录执行 `./cmd appbuild` 编译
1. 在项目目录执行 `./cmd appbuild [build|setting]` 编译
2. 进入 `resources/mobile` eeui框架内打包Android或iOS应用
## 2、PC/Mac客户端
#### 2.1、说明
目录 `electron`,使用`electron`框架遵从electron的开发文档进行打包客户端
#### 2.2、编译客户端
在项目目录执行 `./cmd electron` 根据提示编译
在项目目录执行 `./cmd electron [dev]` 根据提示编译

View File

@@ -2,7 +2,7 @@
**[English](./README.md)** | 中文文档
- [截图预览](README_PREVIEW.md)
- [截图预览](./README_PREVIEW.md)
- [演示站点](http://www.dootask.com/)
**QQ交流群**
@@ -12,8 +12,9 @@
## 安装程序
- 必须安装:`Docker v20.10+``Docker Compose v2.0+`
- 支持环境:`Centos/Debian/Ubuntu/macOS`
- 支持环境:`Centos/Debian/Ubuntu/macOS/Windows`
- 硬件建议2核4G以上
- 特别说明Windows 用户请使用 `git bash` 或者 `cmder` 运行命令
### 部署项目Pro版
@@ -28,7 +29,7 @@ git clone -b pro --depth=1 https://gitee.com/aipaw/dootask.git
# 2、进入目录
cd dootask
# 3、一键安装项目自定义端口安装 ./cmd install --port 2222
# 3、一键安装项目自定义端口安装,如:./cmd install --port 80
./cmd install
```
@@ -42,7 +43,8 @@ cd dootask
### 更换端口
```bash
./cmd port 2222
# 此方法仅更换http端口更换https端口请阅读下面SSL配置
./cmd port 80
```
### 更换URL
@@ -66,11 +68,13 @@ cd dootask
### 开发编译
- 请确保你已经安装了 `NodeJs 20+`
```bash
# 开发模式仅限macOS
# 开发模式
./cmd dev
# 编译项目仅限macOS
# 编译项目这是网页端的App/Pc/Mac客户端请查看 README_CLIENT.md
./cmd prod
```
@@ -85,11 +89,19 @@ cd dootask
./cmd redis "your command" # 运行 redis 命令
./cmd composer "your command" # 运行 composer 命令
./cmd supervisorctl "your command" # 运行 supervisorctl 命令
./cmd test "your command" # 运行 phpunit 命令
./cmd mysql "your command" # 运行 mysql 命令 (backup: 备份数据库recovery: 还原数据库)
```
### NGINX 代理 SSL
### SSL 配置
#### 方法1自动配置
```bash
# 在项目下运行命令,根据提示执行即可
./cmd https
```
#### 或者方法2Nginx 代理配置
```bash
# 1、Nginx 代理配置添加
@@ -97,8 +109,8 @@ proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 2、在项目下运行命令
./cmd https
# 2、在项目下运行命令(如果取消 Nginx 代理配置请运行:./cmd https close
./cmd https agent
```
## 升级更新

View File

@@ -22,5 +22,5 @@
## 编译App
1. 执行 `./cmd appbuild``./cmd appbuild setting` 编译
1. 执行 `./cmd appbuild [setting]` 编译
2. 进入 `resources/mobile` eeui框架内打包Android或iOS应用

View File

@@ -207,7 +207,7 @@ class ApproveController extends AbstractController
if ($id != $user->userid) {
$dialog = WebSocketDialog::checkUserDialog($botUser, $id);
$processInst['comment_user_id'] = $user->userid;
$processInst['comment_content'] = json_decode($data['content'], true)['content'];
$processInst['comment_contents'] = json_decode($data['content'], true) ?? [];
$this->approveMsg('approve_comment_notifier', $dialog, $botUser, $processInst, $processInst);
}
}
@@ -858,7 +858,7 @@ class ApproveController extends AbstractController
BillExport::create()->setTitle($title)->setHeadings($headings)->setData($datas)->setStyles(["A1:Y1" => ["font" => ["bold" => true]]])
];
//
$fileName = '审批记录_' . Base::time() . '.xls';
$fileName = '审批记录_' . Base::time() . '.xlsx';
$filePath = "temp/approve/export/" . date("Ym", Base::time());
$export = new BillMultipleExport($sheets);
$res = $export->store($filePath . "/" . $fileName);
@@ -866,7 +866,7 @@ class ApproveController extends AbstractController
return Base::retError('导出失败,' . $fileName . '');
}
$xlsPath = storage_path("app/" . $filePath . "/" . $fileName);
$zipFile = "app/" . $filePath . "/" . Base::rightDelete($fileName, '.xls') . ".zip";
$zipFile = "app/" . $filePath . "/" . Base::rightDelete($fileName, '.xlsx') . ".zip";
$zipPath = storage_path($zipFile);
if (file_exists($zipPath)) {
Base::deleteDirAndFile($zipPath, true);
@@ -983,7 +983,8 @@ class ApproveController extends AbstractController
'end_day_of_week' => '周' . Base::getTimeWeek(Carbon::parse($process['var']['end_time'])->timestamp),
'description' => $process['var']['description'],
'comment_nickname' => $process['comment_user_id'] ? User::userid2nickname($process['comment_user_id']) : '',
'comment_content' => $process['comment_content'] ?? ''
'comment_content' => $process['comment_contents']['content'] ?? '',
'comment_pictures' => $process['comment_contents']['pictures'] ?? []
];
$text = view('push.bot', ['type' => $type, 'action' => $action, 'is_finished' => $process['is_finished'], 'data' => (object)$data])->render();
$text = preg_replace("/^\x20+/", "", $text);

View File

@@ -58,29 +58,36 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/unread 02. 未读对话列表
* @api {get} api/dialog/beyond 02. 列表外对话
*
* @apiDescription 需要token身份
* @apiDescription 需要token身份,列表外的未读对话 和 列表外的待办对话
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName lists
* @apiName beyond
*
* @apiParam {String} before_at 在这个时间之前未读的数据
* @apiParam {String} unread_at 在这个时间之前未读的数据
* - 格式12021-01-01 00:00:00
* - 格式21612051200
* @apiParam {String} todo_at 在这个时间之前待办的数据
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function unread()
public function beyond()
{
$user = User::auth();
//
$beforeAt = Request::input('before_at');
if (empty($beforeAt)) {
return Base::retError('参数错误');
}
$unreadAt = Request::input('unread_at');
$todoAt = Request::input('todo_at');
//
$data = WebSocketDialog::getDialogUnread($user->userid, Carbon::parse($beforeAt));
$unreadAt = Base::isNumber($unreadAt) ? intval($unreadAt) : trim($unreadAt);
$unreadAt = Carbon::parse($unreadAt)->setTimezone(config('app.timezone'));
//
$todoAt = Base::isNumber($todoAt) ? intval($todoAt) : trim($todoAt);
$todoAt = Carbon::parse($todoAt)->setTimezone(config('app.timezone'));
//
$data = WebSocketDialog::getDialogBeyond($user->userid, $unreadAt, $todoAt);
//
return Base::retSuccess('success', $data);
}
@@ -108,12 +115,12 @@ class DialogController extends AbstractController
return Base::retError('请输入搜索关键词');
}
// 搜索会话
$dialogs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.color', 'u.updated_at as user_at'])
$dialogs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', '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)
->orderByDesc('u.top_at')
->orderByDesc('web_socket_dialogs.last_at')
->orderByDesc('u.last_at')
->take(20)
->get();
$dialogs->transform(function (WebSocketDialog $item) use ($user) {
@@ -145,7 +152,7 @@ class DialogController extends AbstractController
}
// 搜索消息会话
if (count($list) < 20) {
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.color', 'u.updated_at as user_at', 'm.id as search_msg_id'])
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', '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)
@@ -178,7 +185,7 @@ class DialogController extends AbstractController
{
$user = User::auth();
// 搜索会话
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.color', 'u.updated_at as user_at', 'm.id as search_msg_id'])
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', '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)
@@ -213,7 +220,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', 'u.color', 'u.updated_at as user_at'])
$item = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', '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)
@@ -249,8 +256,8 @@ class DialogController extends AbstractController
//
$dialog = WebSocketDialog::checkDialog($dialog_id);
//
$data = $dialog->dialogUser->toArray();
if ($getuser === 1) {
$data = $dialog->dialogUser->toArray();
$array = [];
foreach ($data as $item) {
$res = User::userid2basic($item['userid']);
@@ -258,14 +265,16 @@ class DialogController extends AbstractController
$array[] = array_merge($item, $res->toArray());
}
}
$data = $array;
}
//
$array = [];
foreach ($data as $item) {
if ($item['userid'] > 0) {
$array[] = $item;
}
$array = array_filter($array, function ($item) {
return $item['userid'] > 0;
});
} else {
$data = WebSocketDialogUser::select(['web_socket_dialog_users.*', 'users.bot'])
->join('users', 'web_socket_dialog_users.userid', '=', 'users.userid')
->where('web_socket_dialog_users.dialog_id', $dialog_id)
->orderBy('web_socket_dialog_users.id')
->get();
$array = $data->toArray();
}
return Base::retSuccess('success', $array);
}
@@ -330,6 +339,41 @@ class DialogController extends AbstractController
]);
}
/**
* @api {get} api/dialog/hide 08. 会话隐藏
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName hide
*
* @apiParam {Number} dialog_id 会话ID
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function hide()
{
$user = User::auth();
$dialogId = intval(Request::input('dialog_id'));
$dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first();
if (!$dialogUser) {
return Base::retError("会话不存在");
}
if ($dialogUser->top_at) {
return Base::retError("置顶会话无法隐藏");
}
$dialogUser->hide = 1;
$dialogUser->save();
return Base::retSuccess("success", [
'id' => $dialogUser->dialog_id,
'hide' => 1,
]);
}
/**
* @api {get} api/dialog/tel 09. 获取对方联系电话
*
@@ -856,18 +900,7 @@ class DialogController extends AbstractController
public function msg__sendtext()
{
$user = User::auth();
//
if (!$user->bot) {
$chatInformation = Base::settingFind('system', 'chat_information');
if ($chatInformation == 'required') {
if (empty($user->getRawOriginal('nickname'))) {
return Base::retError('请设置昵称', [], -2);
}
if (empty($user->getRawOriginal('tel'))) {
return Base::retError('请设置联系电话', [], -3);
}
}
}
$user->checkChatInformation();
//
$dialog_id = intval(Request::input('dialog_id'));
$dialog_ids = trim(Request::input('dialog_ids'));
@@ -959,6 +992,7 @@ class DialogController extends AbstractController
public function msg__sendrecord()
{
$user = User::auth();
$user->checkChatInformation();
//
$dialog_id = intval(Request::input('dialog_id'));
$reply_id = intval(Request::input('reply_id'));
@@ -1008,6 +1042,7 @@ class DialogController extends AbstractController
public function msg__sendfile()
{
$user = User::auth();
//
$dialogIds = [intval(Request::input('dialog_id'))];
$replyId = intval(Request::input('reply_id'));
$imageAttachment = intval(Request::input('image_attachment'));
@@ -1040,6 +1075,7 @@ class DialogController extends AbstractController
public function msg__sendfiles()
{
$user = User::auth();
//
$files = Request::file('files');
$image64 = Request::input('image64');
$fileName = Request::input('filename');
@@ -1683,6 +1719,12 @@ class DialogController extends AbstractController
if (Base::isSuccess($res)) {
$add = $res['data'];
}
//
$msg->webSocketDialog?->pushMsg('update', [
'id' => $msg->id,
'todo' => $msg->todo,
'dialog_id' => $msg->dialog_id,
]);
}
}
//
@@ -2141,43 +2183,66 @@ class DialogController extends AbstractController
$dialog_id = intval(Request::input('dialog_id'));
$uuid = trim(Request::input('uuid'));
$text = trim(Request::input('text'));
$list = Request::input('list');
$list = Request::input('list') ?? [];
//
WebSocketDialog::checkDialog($dialog_id);
$strlen = mb_strlen($text);
$noimglen = mb_strlen(preg_replace("/<img[^>]*?>/i", "", $text));
if ($strlen < 1) {
if ($strlen < 1 || empty($list)) {
return Base::retError('内容不能为空');
}
if ($noimglen > 200000) {
return Base::retError('内容最大不能超过200000字');
}
//
$userid = $user->userid;
if ($uuid) {
$dialogMsg = WebSocketDialogMsg::whereDialogId($dialog_id)
->whereType('word-chain')
->orderByDesc('created_at')
->where('msg', 'like', "%$uuid%")
->value('msg');
$list = array_reverse(array_merge($dialogMsg['list'] ?? [], $list));
$list = array_reduce($list, function ($result, $item) {
$fieldValue = $item['id']; // 指定字段名
if (!isset($result[$fieldValue])) {
$result[$fieldValue] = $item;
return AbstractModel::transaction(function () use ($user, $uuid, $dialog_id, $list, $text) {
if ($uuid) {
$dialogMsg = WebSocketDialogMsg::whereDialogId($dialog_id)
->lockForUpdate()
->whereType('word-chain')
->orderByDesc('created_at')
->where('msg', 'like', "%$uuid%")
->value('msg');
//
$createId = $dialogMsg['createid'] ?? $user->userid;
// 新增
$msgList = $dialogMsg['list'] ?? [];
$addList = array_udiff($list, $msgList, function($a, $b) {
return ($a['id'] ?? 0) - $b['id'];
});
foreach ($addList as $key => $item) {
$item['id'] = intval(round(microtime(true) * 1000)) + $key;
$msgList[] = $item;
}
return $result;
}, []);
$list = array_reverse(array_values($list));
}
//
$msgData = [
'text' => $text,
'list' => $list,
'userid' => $userid,
'uuid' => $uuid ?: Base::generatePassword(36),
];
return WebSocketDialogMsg::sendMsg(null, $dialog_id, 'word-chain', $msgData, $user->userid);
// 编辑更新
$lists = array_column($list,null,'id');
foreach ($msgList as $key => $item) {
if (isset($lists[$item['id']]) && $item['userid'] == $user->userid) {
$msgList[$key] = $lists[$item['id']];
}
}
$list = $msgList;
} else {
$createId = $user->userid;
$uuid = Base::generatePassword(36);
foreach ($list as $key => $item) {
$list[$key]['id'] = intval(round(microtime(true) * 1000)) + $key;
}
}
//
usort($list, function ($a, $b) {
return $a['id'] - $b['id'];
});
//
$msgData = [
'text' => $text,
'list' => $list,
'userid' => $user->userid,
'createid' => $createId,
'uuid' => $uuid,
];
return WebSocketDialogMsg::sendMsg(null, $dialog_id, 'word-chain', $msgData, $user->userid);
});
}
/**
@@ -2218,8 +2283,6 @@ class DialogController extends AbstractController
WebSocketDialog::checkDialog($dialog_id);
//
$action = null;
$userid = $user->userid;
$result = [];
if ($type != 'create') {
if ($type == 'vote' && empty($votes)) {
return Base::retError('参数错误');
@@ -2227,42 +2290,50 @@ class DialogController extends AbstractController
if (empty($uuid)) {
return Base::retError('参数错误');
}
$dialogMsgs = WebSocketDialogMsg::whereDialogId($dialog_id)
->whereType('vote')
->orderByDesc('created_at')
->where('msg', 'like', "%$uuid%")
->get();
//
if ($type == 'again') {
$res = WebSocketDialogMsg::sendMsg(null, $dialog_id, 'vote', $dialogMsgs[0]->msg, $user->userid);
if (Base::isError($res)) {
return $res;
}
$result[] = $res['data'];
} else {
foreach ($dialogMsgs as $dialogMsg) {
$action = "change-{$dialogMsg->id}";
$msgData = $dialogMsg->msg;
if ($type == 'finish') {
$msgData['state'] = 0;
} else {
$msgDataVotes = $msgData['votes'] ?? [];
if (in_array($userid, array_column($msgDataVotes, 'userid'))) {
return Base::retError('不能重复投票');
}
$msgDataVotes[] = [
'userid' => $userid,
'votes' => $votes,
];
$msgData['votes'] = $msgDataVotes;
}
$res = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'vote', $msgData, $user->userid);
return AbstractModel::transaction(function () use ($user, $uuid, $dialog_id, $type, $votes) {
//
$dialogMsgs = WebSocketDialogMsg::whereDialogId($dialog_id)
->lockForUpdate()
->whereType('vote')
->orderByDesc('created_at')
->where('msg', 'like', "%$uuid%")
->get();
//
$result = [];
if ($type == 'again') {
$res = WebSocketDialogMsg::sendMsg(null, $dialog_id, 'vote', $dialogMsgs[0]->msg, $user->userid);
if (Base::isError($res)) {
return $res;
}
$result[] = $res['data'];
} else {
foreach ($dialogMsgs as $dialogMsg) {
$action = "change-{$dialogMsg->id}";
$msgData = $dialogMsg->msg;
if ($type == 'finish') {
$msgData['state'] = 0;
} else {
$msgDataVotes = $msgData['votes'] ?? [];
if (in_array($user->userid, array_column($msgDataVotes, 'userid'))) {
return Base::retError('不能重复投票');
}
$msgDataVotes[] = [
'userid' => $user->userid,
'votes' => $votes,
];
$msgData['votes'] = $msgDataVotes;
}
//
$res = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'vote', $msgData, $user->userid);
if (Base::isError($res)) {
return $res;
}
$result[] = $res['data'];
}
}
}
//
return Base::retSuccess('发送成功', $result);
});
} else {
$strlen = mb_strlen($text);
$noimglen = mb_strlen(preg_replace("/<img[^>]*?>/i", "", $text));
@@ -2275,7 +2346,7 @@ class DialogController extends AbstractController
$msgData = [
'text' => $text,
'list' => $list,
'userid' => $userid,
'userid' => $user->userid,
'uuid' => $uuid ?: Base::generatePassword(36),
'multiple' => $multiple,
'anonymous' => $anonymous,
@@ -2286,9 +2357,8 @@ class DialogController extends AbstractController
if (Base::isError($res)) {
return $res;
}
$result[] = $res['data'];
return Base::retSuccess('发送成功', [$res['data']]);
}
return Base::retSuccess('发送成功', $result);
}
/**

View File

@@ -699,6 +699,9 @@ class FileController extends AbstractController
* @apiName content__upload
*
* @apiParam {Number} [pid] 父级ID
* @apiParam {Number} [cover] 覆盖已存在的文件
* - 0不覆盖保留两者默认
* - 1覆盖
* @apiParam {String} [files] 文件名
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
@@ -709,8 +712,9 @@ class FileController extends AbstractController
{
$user = User::auth();
$pid = intval(Request::input('pid'));
$overwrite = intval(Request::input('cover'));
$webkitRelativePath = Request::input('webkitRelativePath');
$data = (new File)->contentUpload($user, $pid, $webkitRelativePath);
$data = (new File)->contentUpload($user, $pid, $webkitRelativePath, $overwrite);
return Base::retSuccess($data['data']['name'] . ' 上传成功', $data['addItem']);
}

View File

@@ -2301,7 +2301,7 @@ class ProjectController extends AbstractController
$project = Project::userProject($task->project_id);
ProjectPermission::userTaskPermission($project, ProjectPermission::TASK_MOVE, $task);
//
if( $task->project_id == $project_id && $task->column_id == $column_id){
if ($task->project_id == $project_id && $task->column_id == $column_id) {
return Base::retSuccess('移动成功', ['id' => $task_id]);
}
//
@@ -2310,7 +2310,7 @@ class ProjectController extends AbstractController
if (empty($column)) {
return Base::retError('列表不存在');
}
if($flow_item_id){
if ($flow_item_id) {
$flowItem = projectFlowItem::whereProjectId($project->id)->whereId($flow_item_id)->first();
if (empty($flowItem)) {
return Base::retError('任务状态不存在');
@@ -2463,6 +2463,21 @@ class ProjectController extends AbstractController
'week' => Doo::translate("" . Base::getTimeWeek($timestamp)),
'segment' => Doo::translate(Base::getTimeDayeSegment($timestamp)),
];
$record = Base::json2array($log->record);
if (is_array($record['change'])) {
foreach ($record['change'] as &$item) {
$item = preg_replace_callback('/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/', function ($matches) {
$time = strtotime($matches[0]);
$second = date("s", $time);
$second = $second === "00" ? "" : ":$second";
if (date("Y") === date("Y", $time)) {
return date("m-d H:i", $time) . $second;
}
return date("Y-m-d H:i", $time) . $second;
}, $item);
}
$log->record = $record;
}
return $log;
});
//

View File

@@ -316,13 +316,16 @@ class ReportController extends AbstractController
// 未完成的任务
$unfinishedContent = "";
$unfinished_task = ProjectTask::query()
->whereNull("complete_at")
->whereNotNull("start_at")
->where("end_at", "<", $end_time->toDateTimeString())
->join("projects", "projects.id", "=", "project_tasks.project_id")
->whereNull("projects.archived_at")
->whereNull("project_tasks.complete_at")
->whereNotNull("project_tasks.start_at")
->where("project_tasks.end_at", "<", $end_time->toDateTimeString())
->whereHas("taskUser", function ($query) use ($user) {
$query->where("userid", $user->userid);
})
->orderByDesc("id")
->select("project_tasks.*")
->orderByDesc("project_tasks.id")
->get();
if ($unfinished_task->isNotEmpty()) {
foreach ($unfinished_task as $task) {

View File

@@ -40,7 +40,7 @@ class SystemController extends AbstractController
* @apiParam {String} type
* - get: 获取(默认)
* - all: 获取所有(需要管理员权限)
* - save: 保存设置(参数:['reg', 'reg_identity', 'reg_invite', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'anon_message', 'auto_archived', 'archived_day', 'task_visible', 'task_default_time', 'all_group_mute', 'all_group_autoin', 'image_compress', 'image_save_local', 'start_home']
* - save: 保存设置(参数:['reg', 'reg_identity', 'reg_invite', 'temp_account_alias', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'anon_message', 'e2e_message', 'auto_archived', 'archived_day', 'task_visible', 'task_default_time', 'all_group_mute', 'all_group_autoin', 'user_private_chat_mute', 'user_group_chat_mute', 'image_compress', 'image_save_local', 'start_home']
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@@ -60,17 +60,21 @@ class SystemController extends AbstractController
'reg',
'reg_identity',
'reg_invite',
'temp_account_alias',
'login_code',
'password_policy',
'project_invite',
'chat_information',
'anon_message',
'e2e_message',
'auto_archived',
'archived_day',
'task_visible',
'task_default_time',
'all_group_mute',
'all_group_autoin',
'user_private_chat_mute',
'user_group_chat_mute',
'image_compress',
'image_save_local',
'start_home',
@@ -96,30 +100,35 @@ class SystemController extends AbstractController
//
if ($type == 'all' || $type == 'save') {
User::auth('admin');
$setting['reg_invite'] = $setting['reg_invite'] ?: Base::generatePassword(8);
$setting['reg_invite'] = $setting['reg_invite'] ?: Base::generatePassword();
} else {
if (isset($setting['reg_invite'])) unset($setting['reg_invite']);
}
//
$setting['reg'] = $setting['reg'] ?: 'open';
$setting['reg_identity'] = $setting['reg_identity'] ?: 'normal';
$setting['temp_account_alias'] = $setting['temp_account_alias'] ?: '';
$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['e2e_message'] = $setting['e2e_message'] ?: 'close';
$setting['auto_archived'] = $setting['auto_archived'] ?: 'close';
$setting['archived_day'] = floatval($setting['archived_day']) ?: 7;
$setting['task_visible'] = $setting['task_visible'] ?: 'close';
$setting['task_default_time'] = $setting['task_default_time'] ? Base::json2array($setting['task_default_time']) : ['09:00', '18:00'];
$setting['all_group_mute'] = $setting['all_group_mute'] ?: 'open';
$setting['all_group_autoin'] = $setting['all_group_autoin'] ?: 'yes';
$setting['user_private_chat_mute'] = $setting['user_private_chat_mute'] ?: 'open';
$setting['user_group_chat_mute'] = $setting['user_group_chat_mute'] ?: 'open';
$setting['image_compress'] = $setting['image_compress'] ?: 'open';
$setting['image_save_local'] = $setting['image_save_local'] ?: 'open';
$setting['start_home'] = $setting['start_home'] ?: 'close';
$setting['file_upload_limit'] = $setting['file_upload_limit'] ?: '';
$setting['unclaimed_task_reminder'] = $setting['unclaimed_task_reminder'] ?: 'close';
$setting['unclaimed_task_reminder_time'] = $setting['unclaimed_task_reminder_time'] ?: '';
$setting['server_version'] = Base::getVersion();
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
@@ -195,7 +204,7 @@ class SystemController extends AbstractController
*
* @apiParam {String} type
* - get: 获取(默认)
* - save: 保存设置(参数:['open', 'appid', 'app_certificate']
* - save: 保存设置(参数:['open', 'appid', 'app_certificate', 'api_key', 'api_secret']
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
@@ -215,12 +224,14 @@ class SystemController extends AbstractController
'open',
'appid',
'app_certificate',
'api_key',
'api_secret',
])) {
unset($all[$key]);
}
}
if ($all['open'] === 'open' && (!$all['appid'] || !$all['app_certificate'])) {
return Base::retError('请填写完整的参数');
return Base::retError('请填写基本配置');
}
$setting = Base::setting('meetingSetting', Base::newTrim($all));
} else {
@@ -231,6 +242,8 @@ class SystemController extends AbstractController
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);
$setting['api_key'] = substr($setting['api_key'], 0, 4) . str_repeat('*', strlen($setting['api_key']) - 8) . substr($setting['api_key'], -4);
$setting['api_secret'] = substr($setting['api_secret'], 0, 4) . str_repeat('*', strlen($setting['api_secret']) - 8) . substr($setting['api_secret'], -4);
}
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
@@ -267,7 +280,10 @@ class SystemController extends AbstractController
'wenxin_secret',
'wenxin_model',
'qianwen_key',
'qianwen_model'
'qianwen_model',
'gemini_key',
'gemini_model',
'gemini_agency',
];
if ($type == 'save') {
@@ -307,11 +323,18 @@ class SystemController extends AbstractController
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "设置成功"], $botUser->userid, true, false, true);
}
}
if ($backup['gemini_key'] != $setting['gemini_key']) {
$botUser = User::botGetOrCreate('ai-gemini');
if ($botUser && $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid)) {
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "设置成功"], $botUser->userid, true, false, true);
}
}
}
//
$setting['openai_model'] = $setting['openai_model'] ?: 'gpt-3.5-turbo';
$setting['wenxin_model'] = $setting['wenxin_model'] ?: 'eb-instant';
$setting['qianwen_model'] = $setting['qianwen_model'] ?: 'qwen-v1';
$setting['gemini_model'] = $setting['gemini_model'] ?: 'gemini-1.0-pro';
if (env("SYSTEM_SETTING") == 'disabled') {
foreach ($keys as $item) {
if (strlen($setting[$item]) > 12) {
@@ -385,6 +408,9 @@ class SystemController extends AbstractController
$setting['edit'] = $setting['edit'] ?: 'close';
$setting['modes'] = is_array($setting['modes']) ? $setting['modes'] : [];
$setting['cmd'] = "curl -sSL '" . Base::fillUrl("api/public/checkin/install?key={$setting['key']}") . "' | sh";
if (Base::judgeClientVersion('0.34.67')) {
$setting['cmd'] = base64_encode($setting['cmd']);
}
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
@@ -1191,7 +1217,7 @@ class SystemController extends AbstractController
if (count($users) > 1) {
$fileName .= "" . count($userid) . "位成员";
}
$fileName .= '签到记录_' . Base::time() . '.xls';
$fileName .= '签到记录_' . Base::time() . '.xlsx';
$filePath = "temp/checkin/export/" . date("Ym", Base::time());
$export = new BillMultipleExport($sheets);
$res = $export->store($filePath . "/" . $fileName);
@@ -1199,7 +1225,7 @@ class SystemController extends AbstractController
return Base::retError('导出失败,' . $fileName . '');
}
$xlsPath = storage_path("app/" . $filePath . "/" . $fileName);
$zipFile = "app/" . $filePath . "/" . Base::rightDelete($fileName, '.xls') . ".zip";
$zipFile = "app/" . $filePath . "/" . Base::rightDelete($fileName, '.xlsx') . ".zip";
$zipPath = storage_path($zipFile);
if (file_exists($zipPath)) {
Base::deleteDirAndFile($zipPath, true);
@@ -1256,8 +1282,14 @@ class SystemController extends AbstractController
* @apiGroup system
* @apiName version
*
* @apiSuccess {String} version
* @apiSuccess {String} publish
* @apiSuccessExample {json} Success-Response:
{
"version": "0.0.1",
"publish": {
"provider": "generic",
"url": ""
}
}
*/
public function version()
{
@@ -1279,4 +1311,52 @@ class SystemController extends AbstractController
}
return $array;
}
/**
* @api {get} api/system/prefetch 25. 预加载的资源
*
* @apiVersion 1.0.0
* @apiGroup system
* @apiName prefetch
*
* @apiSuccessExample {array} Success-Response:
[
"https://......",
"https://......",
"......",
]
*/
public function prefetch()
{
$userAgent = strtolower(Request::server('HTTP_USER_AGENT'));
$isMain = str_contains($userAgent, 'maintaskwindow');
$isApp = str_contains($userAgent, 'kuaifan_eeui');
$version = Base::getVersion();
$array = [];
if ($isMain || $isApp) {
$path = 'js/build/';
$list = Base::readDir(public_path($path), false);
foreach ($list as $item) {
if (is_file($item) && filesize($item) > 50 * 1024) {
$array[] = $path . basename($item);
}
}
}
if ($isMain) {
$file = base_path('.prefetch');
if (file_exists($file)) {
$content = file_get_contents($file);
$items = explode("\n", $content);
$array = array_merge($array, $items);
}
}
return array_map(function($item) use ($version) {
$url = trim($item);
$url = str_replace('{version}', $version, $url);
return url($url);
}, array_values(array_filter($array)));
}
}

View File

@@ -582,7 +582,7 @@ class UsersController extends AbstractController
$tags[] = Doo::translate("系统管理员");
}
if ($userInfo->isTemp()) {
$tags[] = Doo::translate("临时帐号");
$tags[] = User::tempAccountAlias(); // 临时帐号
}
if ($userInfo->userid > 3 && Carbon::parse($userInfo->created_at)->isAfter(Carbon::now()->subDays(30))) {
$tags[] = Doo::translate("新帐号");
@@ -615,7 +615,7 @@ class UsersController extends AbstractController
public function basic()
{
$sharekey = Request::header('sharekey');
if(empty($sharekey) || !Meeting::getShareInfo($sharekey)){
if (empty($sharekey) || !Meeting::getShareInfo($sharekey)) {
User::auth();
}
//
@@ -768,7 +768,7 @@ class UsersController extends AbstractController
//
if ($getCheckinMac) {
$list->transform(function (User $user) use ($getCheckinMac) {
if($getCheckinMac){
if ($getCheckinMac) {
$user->checkin_macs = UserCheckinMac::select(['id', 'mac', 'remark'])->whereUserid($user->userid)->orderBy('id')->get();
}
return $user;
@@ -1101,10 +1101,17 @@ class UsersController extends AbstractController
];
$row = UmengAlias::where($inArray);
if ($row->exists()) {
$row->update(['updated_at' => Carbon::now()]);
$row->update([
'ua' => $data['userAgent'],
'device' => $data['deviceModel'],
'updated_at' => Carbon::now()
]);
return Base::retSuccess('别名已存在');
}
$row = UmengAlias::createInstance($inArray);
$row = UmengAlias::createInstance(array_merge($inArray, [
'ua' => $data['userAgent'],
'device' => $data['deviceModel'],
]));
if ($row->save()) {
return Base::retSuccess('添加成功');
} else {
@@ -1136,17 +1143,17 @@ class UsersController extends AbstractController
public function meeting__open()
{
$type = trim(Request::input('type'));
$meetingid = trim(Request::input('meetingid'));
$meetingid = str_replace(' ', '', trim(Request::input('meetingid')));
$name = trim(Request::input('name'));
$userids = Request::input('userids');
$sharekey = trim(Request::input('sharekey'));
$username = trim(Request::input('username'));
$user = null;
if(!empty($sharekey) && $type === 'join'){
if(!Meeting::getShareInfo($sharekey)){
if (!empty($sharekey) && $type === 'join') {
if (!Meeting::getShareInfo($sharekey)) {
return Base::retError('分享链接已过期');
}
}else{
} else {
$user = User::auth();
}
$isCreate = false;
@@ -1156,6 +1163,11 @@ class UsersController extends AbstractController
if (empty($meeting)) {
return Base::retError('频道ID不存在');
}
if ($meeting->end_at) {
return Base::retError('会议已结束');
}
$meeting->updated_at = Carbon::now();
$meeting->save();
} elseif ($type === 'create') {
$meetingid = strtoupper(Base::generatePassword(11, 1));
$name = $name ?: "{$user?->nickname} 发起的会议";
@@ -1180,9 +1192,9 @@ class UsersController extends AbstractController
if (empty($meetingSetting['appid']) || empty($meetingSetting['app_certificate'])) {
return Base::retError('会议功能配置错误,请联系管理员');
}
$uid = intval(str_pad(Base::generatePassword(4,1), 9, 8, STR_PAD_LEFT));
if($user){
$uid = intval(str_pad(Base::generatePassword(5,1), 6, 9, STR_PAD_LEFT).$user->userid);
$uid = intval(str_pad(Base::generatePassword(4, 1), 9, 8, STR_PAD_LEFT));
if ($user) {
$uid = intval(str_pad(Base::generatePassword(5, 1), 6, 9, STR_PAD_LEFT) . $user->userid);
}
try {
$service = new AgoraTokenGenerator($meetingSetting['appid'], $meetingSetting['app_certificate'], $meeting->channel, $uid);
@@ -1213,7 +1225,7 @@ class UsersController extends AbstractController
//
$data['appid'] = $meetingSetting['appid'];
$data['uid'] = $uid;
$data['userimg'] = $sharekey ? Base::fillUrl('avatar/'.$username.'.png') : $user?->userimg;
$data['userimg'] = $sharekey ? Base::fillUrl('avatar/' . $username . '.png') : $user?->userimg;
$data['nickname'] = $sharekey ? $username : $user?->nickname;
$data['token'] = $token;
$data['msgs'] = $msgs;
@@ -1242,7 +1254,7 @@ class UsersController extends AbstractController
{
$meetingid = trim(Request::input('meetingid'));
$sharekey = trim(Request::input('sharekey'));
if(empty($sharekey) || !Meeting::getShareInfo($sharekey)){
if (empty($sharekey) || !Meeting::getShareInfo($sharekey)) {
User::auth();
}
$meeting = Meeting::whereMeetingid($meetingid)->first();
@@ -1963,7 +1975,7 @@ class UsersController extends AbstractController
}
}
} else {
if($type == 'file'){
if ($type == 'file') {
$lists[] = [
'type' => 'children',
'url' => Base::fillUrl("api/users/share/list") . "?pid=0",

View File

@@ -7,20 +7,22 @@ use Cache;
use Request;
use Redirect;
use Response;
use App\Module\Doo;
use App\Models\File;
use App\Module\Base;
use App\Tasks\LoopTask;
use App\Module\Extranet;
use App\Tasks\AppPushTask;
use App\Module\RandomColor;
use App\Tasks\LoopTask;
use App\Tasks\AppPushTask;
use App\Tasks\JokeSoupTask;
use App\Tasks\DeleteTmpTask;
use App\Tasks\EmailNoticeTask;
use App\Tasks\AutoArchivedTask;
use App\Tasks\DeleteBotMsgTask;
use App\Tasks\CheckinRemindTask;
use Hhxsv5\LaravelS\Swoole\Task\Task;
use App\Tasks\CloseMeetingRoomTask;
use App\Tasks\UnclaimedTaskRemindTask;
use Hhxsv5\LaravelS\Swoole\Task\Task;
use LasseRafn\InitialAvatarGenerator\InitialAvatar;
@@ -37,14 +39,8 @@ class IndexController extends InvokeController
if ($action) {
$app .= "__" . $action;
}
switch ($app) {
case 'manifest.txt':
$app = 'manifest';
$child = 'txt';
break;
case 'default':
return '';
if ($app == 'default') {
return '';
}
if (!method_exists($this, $app)) {
$app = method_exists($this, $method) ? $method : 'main';
@@ -73,49 +69,7 @@ class IndexController extends InvokeController
'version' => Base::getVersion(),
'style' => $style,
'script' => $script,
])->header('Link', "<" . url('manifest.txt') . ">; rel=\"prefetch\"");
}
/**
* Manifest
* @param $child
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|string
*/
public function manifest($child = '')
{
if (empty($child)) {
$murl = url('manifest.txt');
return response($murl)->header('Link', "<{$murl}>; rel=\"prefetch\"");
}
$array = [
"office/web-apps/apps/api/documents/api.js?hash=" . Base::getVersion(),
"office/7.5.1-23/web-apps/vendor/requirejs/require.js",
"office/7.5.1-23/web-apps/apps/api/documents/api.js",
"office/7.5.1-23/sdkjs/common/AllFonts.js",
"office/7.5.1-23/web-apps/vendor/xregexp/xregexp-all-min.js",
"office/7.5.1-23/web-apps/vendor/sockjs/sockjs.min.js",
"office/7.5.1-23/web-apps/vendor/jszip/jszip.min.js",
"office/7.5.1-23/web-apps/vendor/jszip-utils/jszip-utils.min.js",
"office/7.5.1-23/sdkjs/common/libfont/wasm/fonts.js",
"office/7.5.1-23/sdkjs/common/Charts/ChartStyles.js",
"office/7.5.1-23/sdkjs/slide/themes//themes.js",
"office/7.5.1-23/web-apps/apps/presentationeditor/main/app.js",
"office/7.5.1-23/sdkjs/slide/sdk-all-min.js",
"office/7.5.1-23/sdkjs/slide/sdk-all.js",
"office/7.5.1-23/web-apps/apps/documenteditor/main/app.js",
"office/7.5.1-23/sdkjs/word/sdk-all-min.js",
"office/7.5.1-23/sdkjs/word/sdk-all.js",
"office/7.5.1-23/web-apps/apps/spreadsheeteditor/main/app.js",
"office/7.5.1-23/sdkjs/cell/sdk-all-min.js",
"office/7.5.1-23/sdkjs/cell/sdk-all.js",
];
foreach ($array as &$item) {
$item = url($item);
}
return implode(PHP_EOL, $array);
]);
}
/**
@@ -212,6 +166,8 @@ class IndexController extends InvokeController
Task::deliver(new JokeSoupTask());
// 未领取任务通知
Task::deliver(new UnclaimedTaskRemindTask());
// 关闭会议室
Task::deliver(new CloseMeetingRoomTask());
return "success";
}
@@ -222,6 +178,7 @@ class IndexController extends InvokeController
public function desktop__publish($name = '')
{
$publishVersion = Request::header('publish-version');
$fileNum = Request::get('file_num', 1);
$latestFile = public_path("uploads/desktop/latest");
$latestVersion = file_exists($latestFile) ? trim(file_get_contents($latestFile)) : "0.0.1";
if (strtolower($name) === 'latest') {
@@ -229,6 +186,7 @@ class IndexController extends InvokeController
}
// 上传
if (preg_match("/^\d+\.\d+\.\d+$/", $publishVersion)) {
$uploadSuccessFileNum = (int)Cache::get($publishVersion, 0);
$publishKey = Request::header('publish-key');
if ($publishKey !== env('APP_KEY')) {
return Base::retError("key error");
@@ -243,6 +201,20 @@ class IndexController extends InvokeController
]);
if (Base::isSuccess($res)) {
file_put_contents($latestFile, $publishVersion);
$uploadSuccessFileNum = $uploadSuccessFileNum + 1;
Cache::set($publishVersion, $uploadSuccessFileNum, 7200);
}
if ($uploadSuccessFileNum >= $fileNum){
$directoryPath = public_path("uploads/desktop");
$files = array_filter(scandir($directoryPath), function($file) use($directoryPath) {
return preg_match("/^\d+\.\d+\.\d+$/", $file) && is_dir($directoryPath . '/' . $file) && $file != '.' && $file != '..';
});
sort($files);
foreach ($files as $key => $file) {
if ($file != $publishVersion && $key < count($files) - 2) {
Base::deleteDirAndFile($directoryPath . '/' . $file);
}
}
}
return $res;
}
@@ -254,14 +226,15 @@ class IndexController extends InvokeController
$lists = Base::readDir($dirPath);
$files = [];
foreach ($lists as $file) {
if (str_ends_with($file, '.yml') || str_ends_with($file, '.yaml')) {
if (str_ends_with($file, '.yml') || str_ends_with($file, '.yaml') || str_ends_with($file, '.blockmap')) {
continue;
}
$fileName = Base::leftDelete($file, $dirPath);
$fileSize = filesize($file);
$files[] = [
'name' => substr($fileName, 1),
'time' => date("Y-m-d H:i:s", filemtime($file)),
'size' => Base::readableBytes(filesize($file)),
'size' => $fileSize > 0 ? Base::readableBytes($fileSize) : 0,
'url' => Base::fillUrl($path . $fileName),
];
}
@@ -278,10 +251,11 @@ class IndexController extends InvokeController
continue;
}
$fileName = Base::leftDelete($file, $dirPath);
$fileSize = filesize($file);
$apkFile = [
'name' => substr($fileName, 1),
'time' => date("Y-m-d H:i:s", filemtime($file)),
'size' => Base::readableBytes(filesize($file)),
'size' => $fileSize > 0 ? Base::readableBytes($fileSize) : 0,
'url' => Base::fillUrl($path . $fileName),
];
}
@@ -331,61 +305,85 @@ class IndexController extends InvokeController
abort(404);
}
//
if (file_exists($file)) {
parse_str($data['query'], $query);
$name = Arr::get($query, 'name');
$ext = strtolower(Arr::get($query, 'ext'));
$userAgent = strtolower(Request::server('HTTP_USER_AGENT'));
if ($ext === 'pdf') {
// electron 直接在线预览查看
if (str_contains($userAgent, 'electron')) {
return Response::download($file, $name, [
'Content-Type' => 'application/pdf'
], 'inline');
}
// EEUI App 直接在线预览查看
if (str_contains($userAgent, 'eeui') && Base::judgeClientVersion("0.34.47")) {
if (str_contains($userAgent, 'iphone') || str_contains($userAgent, 'ipad')) {
$message = Base::array2json([
'type' => 'currentOpen',
'url' => Base::fillUrl($path),
]);
return "<script>window.top.postMessage($message, '*')</script>";
}
}
}
//
if (in_array($ext, File::localExt)) {
$url = Base::fillUrl($path);
} else {
$url = 'http://' . env('APP_IPPR') . '.3/' . $path;
}
if ($ext !== 'pdf') {
$url = Base::urlAddparameter($url, [
'fullfilename' => $name . '.' . $ext
if (!file_exists($file)) {
abort(404);
}
//
parse_str($data['query'], $query);
$name = Arr::get($query, 'name');
$ext = strtolower(Arr::get($query, 'ext'));
$userAgent = strtolower(Request::server('HTTP_USER_AGENT'));
if ($ext === 'pdf') {
// 文件超过 10m 不支持在线预览,提示下载
if (filesize($file) > 10 * 1024 * 1024) {
return view('download', [
'name' => $name,
'size' => Base::readableBytes(filesize($file)),
'url' => Base::fillUrl($path),
'button' => Doo::translate('点击下载'),
]);
}
$toUrl = Base::fillUrl("fileview/onlinePreview?url=" . urlencode(base64_encode($url)));
return Redirect::to($toUrl, 301);
// 浏览器类型
$browser = 'none';
if (str_contains($userAgent, 'chrome') || str_contains($userAgent, 'android_kuaifan_eeui')) {
$browser = str_contains($userAgent, 'android_kuaifan_eeui') ? 'android-mobile' : 'chrome-desktop';
} elseif (str_contains($userAgent, 'safari') || str_contains($userAgent, 'ios_kuaifan_eeui')) {
$browser = str_contains($userAgent, 'ios_kuaifan_eeui') ? 'safari-mobile' : 'safari-desktop';
}
// electron 直接在线预览查看
if (str_contains($userAgent, 'electron') || str_contains($browser, 'desktop')) {
return Response::download($file, $name, [
'Content-Type' => 'application/pdf'
], 'inline');
}
// EEUI App 直接在线预览查看
if (str_contains($userAgent, 'eeui') && Base::judgeClientVersion("0.34.47")) {
if ($browser === 'safari-mobile') {
$redirectUrl = Base::fillUrl($path);
return <<<EOF
<script>
window.top.postMessage({
action: "eeuiAppSendMessage",
data: [
{
action: 'setPageData',
data: {
showProgress: true,
titleFixed: true,
urlFixed: true,
}
},
{
action: 'createTarget',
url: "{$redirectUrl}",
}
]
}, "*")
</script>
EOF;
}
}
}
abort(404);
//
if (in_array($ext, File::localExt)) {
$url = Base::fillUrl($path);
} else {
$url = 'http://' . env('APP_IPPR') . '.3/' . $path;
}
$url = Base::urlAddparameter($url, [
'fullfilename' => Base::rightDelete($name, '.' . $ext) . '_' . filemtime($file) . '.' . $ext
]);
$redirectUrl = Base::fillUrl("fileview/onlinePreview?url=" . urlencode(base64_encode($url)));
return Redirect::to($redirectUrl, 301);
}
/**
* 保存配置
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|string
* 保存配置 (todo 已废弃)
* @return string
*/
public function storage__synch()
{
$key = Request::input('key');
$value = Request::input('value');
if ($key) {
$value = [$key => $value];
}
if (!is_array($value)) {
$value = Base::json2array($value);
}
return view('storage', ['value' => Base::array2json($value)]);
return '<!-- Deprecated -->';
}
/**

View File

@@ -15,9 +15,6 @@ class VerifyCsrfToken extends Middleware
// 接口部分
'api/*',
// 保存配置
'storage/synch/',
// 发布桌面端
'desktop/publish/',
];

View File

@@ -11,9 +11,15 @@ namespace App\Models;
* @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|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @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|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @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)

View File

@@ -12,9 +12,15 @@ use Carbon\Carbon;
* @property int|null $did 删除的数据ID
* @property int|null $userid 关系会员ID
* @property \Illuminate\Support\Carbon|null $created_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @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|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @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)

View File

@@ -28,10 +28,16 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|File newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|File newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|File onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|File query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|File whereCid($value)
* @method static \Illuminate\Database\Eloquent\Builder|File whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|File whereCreatedId($value)
@@ -190,9 +196,10 @@ class File extends AbstractModel
* @param user $user
* @param int $pid
* @param string $webkitRelativePath
* @param bool $overwrite
* @return array
*/
public function contentUpload($user, int $pid, $webkitRelativePath)
public function contentUpload($user, int $pid, $webkitRelativePath, $overwrite = false)
{
$userid = $user->userid;
if ($pid > 0) {
@@ -283,17 +290,25 @@ class File extends AbstractModel
if ($data['ext'] == 'markdown') {
$data['ext'] = 'md';
}
$file = File::createInstance([
$file = null;
$params = [
'pid' => $pid,
'name' => Base::rightDelete($data['name'], '.' . $data['ext']),
'type' => $type,
'ext' => $data['ext'],
'userid' => $userid,
'created_id' => $user->userid,
]);
$file->handleDuplicateName();
];
if ($overwrite) {
$file = self::wherePid($params['pid'])->whereExt($params['ext'])->whereName($params['name'])->first();
}
if (!$file) {
$overwrite = false;
$file = File::createInstance($params);
$file->handleDuplicateName();
}
// 开始创建
return AbstractModel::transaction(function () use ($addItem, $webkitRelativePath, $type, $user, $data, $file) {
return AbstractModel::transaction(function () use ($overwrite, $addItem, $webkitRelativePath, $type, $user, $data, $file) {
$file->size = $data['size'] * 1024;
$file->saveBeforePP();
//
@@ -321,11 +336,12 @@ class File extends AbstractModel
$tmpRow->pushMsg('add', $tmpRow);
//
$data = File::handleImageUrl($tmpRow->toArray());
$data['full_name'] = $webkitRelativePath ?: $data['name'];
$data['full_name'] = $webkitRelativePath ?: ($data['name'] . '.' . $data['ext']);
$data['overwrite'] = $overwrite ? 1 : 0;
//
$addItem[] = $data;
return ['data'=>$data,'addItem'=>$addItem];
return ['data' => $data, 'addItem' => $addItem];
});
}

View File

@@ -18,10 +18,16 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|FileContent newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|FileContent newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|FileContent onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|FileContent query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|FileContent whereContent($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileContent whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileContent whereDeletedAt($value)
@@ -70,7 +76,7 @@ class FileContent extends AbstractModel
'name' => $name,
'ext' => $fileExt
]));
return Base::fillUrl("online/preview/{$name}?key={$key}");
return Base::fillUrl("online/preview/{$name}?key={$key}&version=" . Base::getVersion() . "&__=" . Base::msecTime());
}
/**

View File

@@ -8,16 +8,22 @@ use App\Module\Base;
* App\Models\FileLink
*
* @property int $id
* @property int|null $file_id 项目ID
* @property int|null $file_id 文件ID
* @property int|null $num 累计访问
* @property string|null $code 链接码
* @property int|null $userid 会员ID
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\File|null $file
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|FileLink newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|FileLink newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|FileLink query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|FileLink whereCode($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileLink whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileLink whereFileId($value)

View File

@@ -12,9 +12,15 @@ namespace App\Models;
* @property int|null $permission 权限0只读1读写
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|FileUser newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|FileUser newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|FileUser query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|FileUser whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileUser whereFileId($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileUser whereId($value)

View File

@@ -18,9 +18,15 @@ use Illuminate\Support\Carbon;
* @property Carbon|null $updated_at
* @property string|null $end_at
* @property Carbon|null $deleted_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|Meeting newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Meeting newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Meeting query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|Meeting whereChannel($value)
* @method static \Illuminate\Database\Eloquent\Builder|Meeting whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Meeting whereDeletedAt($value)
@@ -44,12 +50,12 @@ class Meeting extends AbstractModel
public function getShareLink()
{
$code = base64_encode("{$this->meetingid}" . Base::generatePassword());
Cache::put(self::CACHE_KEY.'_'.$code, [
Cache::put(self::CACHE_KEY . '_' . $code, [
'id' => $this->id,
'meetingid' => $this->meetingid,
'channel' => $this->channel,
], Carbon::now()->addHours(self::CACHE_EXPIRED_TIME));
return Base::fillUrl("meeting/{$this->meetingid}/".$code);
return Base::fillUrl("meeting/{$this->meetingid}/" . $code);
}
/**
@@ -58,19 +64,19 @@ class Meeting extends AbstractModel
*/
public static function getShareInfo($code)
{
if(Cache::has(self::CACHE_KEY.'_'.$code)){
return Cache::get(self::CACHE_KEY.'_'.$code);
if (Cache::has(self::CACHE_KEY . '_' . $code)) {
return Cache::get(self::CACHE_KEY . '_' . $code);
}
return null;
}
/**
* 保存访客信息
* @return mixed
* @return void
*/
public static function setTouristInfo($data)
{
Cache::put(Meeting::CACHE_KEY.'_'.$data['uid'], [
Cache::put(Meeting::CACHE_KEY . '_' . $data['uid'], [
'uid' => $data['uid'],
'userimg' => $data['userimg'],
'nickname' => $data['nickname'],
@@ -83,8 +89,8 @@ class Meeting extends AbstractModel
*/
public static function getTouristInfo($touristId)
{
if(Cache::has(Meeting::CACHE_KEY.'_'.$touristId)){
return Cache::get(Meeting::CACHE_KEY.'_'.$touristId);
if (Cache::has(Meeting::CACHE_KEY . '_' . $touristId)) {
return Cache::get(Meeting::CACHE_KEY . '_' . $touristId);
}
return null;
}

34
app/Models/MeetingMsg.php Normal file
View File

@@ -0,0 +1,34 @@
<?php
namespace App\Models;
/**
* App\Models\MeetingMsg
*
* @property int $id
* @property string|null $meetingid 会议ID
* @property int|null $dialog_id 对话ID
* @property int|null $msg_id 消息ID
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|MeetingMsg newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|MeetingMsg newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|MeetingMsg query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|MeetingMsg whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|MeetingMsg whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|MeetingMsg whereMeetingid($value)
* @method static \Illuminate\Database\Eloquent\Builder|MeetingMsg whereMsgId($value)
* @mixin \Eloquent
*/
class MeetingMsg extends AbstractModel
{
function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->timestamps = false;
}
}

View File

@@ -36,10 +36,16 @@ use Request;
* @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|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|Project newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Project newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Project onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|Project query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|Project whereArchivedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Project whereArchivedUserid($value)
* @method static \Illuminate\Database\Eloquent\Builder|Project whereCreatedAt($value)

View File

@@ -22,10 +22,16 @@ use Request;
* @property-read \App\Models\Project|null $project
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProjectTask> $projectTask
* @property-read int|null $project_task_count
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn whereColor($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn whereDeletedAt($value)

View File

@@ -2,8 +2,6 @@
namespace App\Models;
use App\Module\Base;
/**
* App\Models\ProjectFlow
*
@@ -14,9 +12,15 @@ use App\Module\Base;
* @property \Illuminate\Support\Carbon|null $updated_at
* @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|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereName($value)

View File

@@ -13,7 +13,7 @@ use App\Module\Base;
* @property string|null $name 名称
* @property string|null $status 状态
* @property array $turns 可流转
* @property array $userids 自动负责人ID
* @property array $userids 状态负责人ID
* @property string|null $usertype 流转模式
* @property int|null $userlimit 限制负责人
* @property int|null $columnid 对应的项目列表
@@ -21,9 +21,15 @@ use App\Module\Base;
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\ProjectFlow|null $projectFlow
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereColumnid($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereFlowId($value)

View File

@@ -13,9 +13,15 @@ namespace App\Models;
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read bool $already
* @property-read \App\Models\Project|null $project
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite whereCode($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite whereId($value)

View File

@@ -10,7 +10,7 @@ use App\Module\Base;
* @property int $id
* @property int|null $project_id 项目ID
* @property int|null $column_id 列表ID
* @property int|null $task_id 项目ID
* @property int|null $task_id 任务ID
* @property int|null $userid 会员ID
* @property string|null $detail 详细信息
* @property array $record 记录数据
@@ -18,9 +18,15 @@ use App\Module\Base;
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\ProjectTask|null $projectTask
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereColumnId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereDetail($value)

View File

@@ -13,9 +13,15 @@ use App\Module\Base;
* @property string $permissions 权限
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectPermission newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectPermission newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectPermission query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectPermission whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectPermission whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectPermission wherePermissions($value)

View File

@@ -63,10 +63,16 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @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|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereArchivedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereArchivedFollow($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereArchivedUserid($value)
@@ -329,13 +335,7 @@ class ProjectTask extends AbstractModel
$query->where(function ($q1) use ($start, $end, $type) {
switch ($type) {
case 'createdTime':
$q1->where(function ($q2) use ($start) {
$q2->where('project_tasks.created_at', '>=', $start);
})->orWhere(function ($q2) use ($end) {
$q2->where('project_tasks.created_at', '<=', $end);
})->orWhere(function ($q2) use ($start, $end) {
$q2->where('project_tasks.created_at', '>', $start)->where('project_tasks.created_at', '<', $end);
});
$q1->where('project_tasks.created_at', '>=', $start)->where('project_tasks.created_at', '<=', $end);
break;
default:
@@ -1430,53 +1430,62 @@ class ProjectTask extends AbstractModel
}
//
$array = [];
if (empty($data['parent_id'])) {
if (Arr::exists($data, 'owner') || Arr::exists($data, 'assist')) {
$taskUser = ProjectTaskUser::select(['userid', 'owner'])->whereTaskId($data['id'])->get();
// 负责人
$owners = $taskUser->where('owner', 1)->pluck('userid')->toArray();
$owners = array_intersect($userids, $owners);
if ($owners) {
$array[] = [
'userid' => array_values($owners),
'data' => array_merge($data, [
'owner' => 1,
'assist' => 1,
])
];
}
// 协助人
$assists = $taskUser->where('owner', 0)->pluck('userid')->toArray();
$assists = array_intersect($userids, $assists);
if ($assists) {
$array[] = [
'userid' => array_values($assists),
'data' => array_merge($data, [
'owner' => 0,
'assist' => 1,
])
];
}
// 项目成员(其他人)
if ($data['visibility'] == 1) {
// 全部可见
if (Arr::exists($data, 'owner') || Arr::exists($data, 'assist')) {
$taskUser = ProjectTaskUser::select(['userid', 'owner'])->whereTaskId($data['id'])->get();
// 负责人
$owners = $taskUser->where('owner', 1)->pluck('userid')->toArray();
$owners = array_intersect($userids, $owners);
if ($owners) {
$array[] = [
'userid' => array_values($owners),
'data' => array_merge($data, [
'owner' => 1,
'assist' => 1,
])
];
}
// 协助人
$assists = $taskUser->where('owner', 0)->pluck('userid')->toArray();
$assists = array_intersect($userids, $assists);
if ($assists) {
$array[] = [
'userid' => array_values($assists),
'data' => array_merge($data, [
'owner' => 0,
'assist' => 1,
])
];
}
// 其他人
switch ($data['visibility']) {
case 1:
// 项目人员,除了负责人、协助人项目其他人
$userids = array_diff($userids, $owners, $assists);
} else {
// 指定可见
$userids = $taskUser->pluck('userid')->toArray();
}
$data = array_merge($data, [
'owner' => 0,
'assist' => 0,
]);
break;
case 2:
// 任务人员,除了负责人、协助人
$userids = [];
break;
case 3:
// 指定成员
$specifys = ProjectTaskVisibilityUser::select(['userid'])->whereTaskId($data['id'])->pluck('userid')->toArray();
$userids = array_diff($specifys, $owners, $assists);
break;
default:
$userids = [];
break;
}
if ($userids) {
$array[] = [
'userid' => array_values($userids),
'data' => array_merge($data, [
'owner' => 0,
'assist' => 0,
])
];
}
}
//
$array[] = [
'userid' => array_values($userids),
'data' => $data
];
//
foreach ($array as $item) {
$params = [
'ignoreFd' => Request::header('fd'),

View File

@@ -14,9 +14,15 @@ use App\Exceptions\ApiException;
* @property string|null $content 内容
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent whereContent($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent whereId($value)

View File

@@ -22,9 +22,15 @@ use Cache;
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read int $height
* @property-read int $width
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereDownload($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereExt($value)

View File

@@ -14,9 +14,15 @@ namespace App\Models;
* @property string|null $after_flow_item_name (变化后)工作流状态名称
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereAfterFlowItemId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereAfterFlowItemName($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereBeforeFlowItemId($value)

View File

@@ -17,10 +17,16 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog whereDeletedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog whereId($value)

View File

@@ -12,9 +12,15 @@ namespace App\Models;
* @property string|null $color 颜色
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag whereColor($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag whereId($value)

View File

@@ -14,9 +14,15 @@ namespace App\Models;
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\ProjectTask|null $projectTask
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser whereOwner($value)

View File

@@ -12,9 +12,15 @@ namespace App\Models;
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\ProjectTask|null $projectTask
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskVisibilityUser newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskVisibilityUser newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskVisibilityUser query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskVisibilityUser whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskVisibilityUser whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskVisibilityUser whereProjectId($value)

View File

@@ -13,9 +13,15 @@ namespace App\Models;
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\Project|null $project
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereOwner($value)

View File

@@ -29,9 +29,15 @@ use JetBrains\PhpStorm\Pure;
* @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 \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static Builder|Report newModelQuery()
* @method static Builder|Report newQuery()
* @method static Builder|Report query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static Builder|Report whereContent($value)
* @method static Builder|Report whereCreatedAt($value)
* @method static Builder|Report whereId($value)

View File

@@ -13,9 +13,15 @@ use Illuminate\Database\Eloquent\Model;
* @property string|null $receive_time 接收时间
* @property int $userid 接收人
* @property int $read 是否已读
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|ReportReceive newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ReportReceive newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ReportReceive query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|ReportReceive whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ReportReceive whereRead($value)
* @method static \Illuminate\Database\Eloquent\Builder|ReportReceive whereReceiveTime($value)

View File

@@ -13,9 +13,15 @@ use App\Module\Base;
* @property string|null $setting
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|Setting newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Setting newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Setting query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|Setting whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Setting whereDesc($value)
* @method static \Illuminate\Database\Eloquent\Builder|Setting whereId($value)

View File

@@ -15,10 +15,16 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker whereArgs($value)
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker whereDeletedAt($value)

View File

@@ -11,9 +11,15 @@ namespace App\Models;
* @property string|null $content
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|Tmp newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Tmp newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Tmp query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|Tmp whereContent($value)
* @method static \Illuminate\Database\Eloquent\Builder|Tmp whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Tmp whereId($value)

View File

@@ -14,15 +14,25 @@ use Hedeqiang\UMeng\IOS;
* @property int|null $userid 会员ID
* @property string|null $alias 别名
* @property string|null $platform 平台类型
* @property string|null $device 设备类型
* @property string|null $ua userAgent
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereAlias($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereDevice($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias wherePlatform($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereUa($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereUserid($value)
* @mixin \Eloquent
@@ -31,6 +41,16 @@ class UmengAlias extends AbstractModel
{
protected $table = 'umeng_alias';
/**
* 推送内容处理
* @param $string
* @return string
*/
private static function specialCharacters($string)
{
return str_replace(["\r\n", "\r", "\n"], '', $string);
}
/**
* 获取推送配置
* @return array|false
@@ -73,9 +93,9 @@ class UmengAlias extends AbstractModel
return false;
}
//
$title = $array['title'] ?: ''; // 标题
$subtitle = $array['subtitle'] ?: ''; // 副标题iOS
$body = $array['body'] ?: ''; // 通知内容
$title = self::specialCharacters($array['title'] ?: ''); // 标题
$subtitle = self::specialCharacters($array['subtitle'] ?: ''); // 副标题iOS
$body = self::specialCharacters($array['body'] ?: ''); // 通知内容
$description = $array['description'] ?: 'no description'; // 描述
$extra = is_array($array['extra']) ? $array['extra'] : []; // 额外参数
$seconds = intval($array['seconds']) ?: 86400; // 有效时间(单位:秒)
@@ -132,7 +152,13 @@ class UmengAlias extends AbstractModel
'mi_activity' => 'app.eeui.umeng.activity.MfrMessageActivity',
'policy' => [
'expire_time' => Carbon::now()->addSeconds($seconds)->toDateTimeString(),
]
],
'channel_properties' => [
'vivo_category' => 'IM',
'huawei_channel_importance' => 'NORMAL',
'huawei_channel_category' => 'IM',
'channel_fcm' => 1,
],
]);
default:

View File

@@ -37,10 +37,16 @@ use Carbon\Carbon;
* @property int|null $bot 是否机器人
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Database\Factories\UserFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|User query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|User whereAz($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereBot($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereChangepass($value)
@@ -290,6 +296,26 @@ class User extends AbstractModel
});
}
/**
* 检查发送聊天内容前必须设置昵称、电话
* @return void
*/
public function checkChatInformation()
{
if ($this->bot) {
return;
}
$chatInformation = Base::settingFind('system', 'chat_information');
if ($chatInformation == 'required') {
if (empty($this->getRawOriginal('nickname'))) {
throw new ApiException('请设置昵称', [], -2);
}
if (empty($this->getRawOriginal('tel'))) {
throw new ApiException('请设置联系电话', [], -3);
}
}
}
/** ***************************************************************************************** */
/** ***************************************************************************************** */
/** ***************************************************************************************** */
@@ -507,6 +533,16 @@ class User extends AbstractModel
}
}
/**
* 临时帐号别名
* @return mixed|string
*/
public static function tempAccountAlias()
{
$alias = Base::settingFind('system', 'temp_account_alias');
return $alias ?: Doo::translate("临时帐号");
}
/**
* 获取头像
* @param $userid
@@ -539,6 +575,8 @@ class User extends AbstractModel
return url("images/avatar/default_openai.png");
case 'ai-claude@bot.system':
return url("images/avatar/default_claude.png");
case 'ai-gemini@bot.system':
return url("images/avatar/default_gemini.png");
case 'bot-manager@bot.system':
return url("images/avatar/default_bot.png");
case 'meeting-alert@bot.system':

View File

@@ -5,6 +5,7 @@ namespace App\Models;
use App\Module\Base;
use App\Module\Doo;
use App\Module\Extranet;
use App\Tasks\JokeSoupTask;
use Cache;
use Carbon\Carbon;
@@ -20,9 +21,15 @@ use Carbon\Carbon;
* @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|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @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|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @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)
@@ -57,6 +64,7 @@ class UserBot extends AbstractModel
'ai-claude' => 'Claude',
'ai-wenxin' => '文心一言',
'ai-qianwen' => '通义千问',
'ai-gemini' => 'Gemini',
'bot-manager' => '机器人管理',
'meeting-alert' => '会议通知',
'okr-alert' => 'OKR提醒',
@@ -117,6 +125,7 @@ class UserBot extends AbstractModel
'ai-openai@bot.system',
'ai-claude@bot.system',
'ai-wenxin@bot.system',
'ai-gemini@bot.system',
'ai-qianwen@bot.system' => [
[
'key' => '%3A.clear',
@@ -227,8 +236,8 @@ class UserBot extends AbstractModel
if ($checkins && $botUser = User::botGetOrCreate('check-in')) {
$getJokeSoup = function($type) {
$pre = $type == "up" ? "每日开心:" : "心灵鸡汤:";
$key = $type == "up" ? "JokeSoupTask:jokes" : "JokeSoupTask:soups";
$array = Base::json2array(Cache::get($key));
$key = $type == "up" ? "jokes" : "soups";
$array = Base::json2array(Cache::get(JokeSoupTask::keyName($key)));
if ($array) {
$item = $array[array_rand($array)];
if ($item) {

View File

@@ -15,9 +15,15 @@ use App\Module\Base;
* @property string|null $remark 备注
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac whereMac($value)

View File

@@ -16,9 +16,15 @@ use App\Module\Base;
* @property int|null $report_time 上报的时间戳
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereDate($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereId($value)

View File

@@ -16,9 +16,15 @@ use App\Module\Base;
* @property string $cache 会员资料缓存
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|UserDelete newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserDelete newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserDelete query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|UserDelete whereCache($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDelete whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDelete whereEmail($value)

View File

@@ -14,9 +14,15 @@ use App\Exceptions\ApiException;
* @property int|null $owner_userid 部门负责人
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|UserDepartment newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserDepartment newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserDepartment query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|UserDepartment whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDepartment whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDepartment whereId($value)

View File

@@ -19,9 +19,15 @@ use Guanguans\Notify\Messages\EmailMessage;
* @property int|null $type 邮件类型1-邮箱认证2-修改邮箱
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification whereCode($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification whereEmail($value)

View File

@@ -16,9 +16,15 @@ use Guanguans\Notify\Messages\EmailMessage;
* @property int|null $new_userid 交接人
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer whereNewUserid($value)

View File

@@ -13,9 +13,15 @@ namespace App\Models;
* @property int|null $userid
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereFd($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereId($value)

View File

@@ -20,29 +20,35 @@ use Illuminate\Support\Facades\DB;
* @property string|null $group_type 聊天室类型
* @property string|null $name 对话名称
* @property string $avatar 头像(群)
* @property string|null $last_at 最后消息时间
* @property int|null $owner_id 群主用户ID
* @property int|null $link_id 关联id
* @property int|null $top_userid 置顶的用户ID
* @property int|null $top_msg_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<int, \App\Models\WebSocketDialogUser> $dialogUser
* @property-read int|null $dialog_user_count
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @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)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereLastAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereLinkId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereOwnerId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereTopMsgId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereTopUserid($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereType($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog withTrashed()
@@ -81,7 +87,7 @@ class WebSocketDialog extends AbstractModel
*/
public static function getDialogList($userid, $updated = "", $deleted = "")
{
$builder = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.color', 'u.updated_at as user_at'])
$builder = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->where('u.userid', $userid);
if ($updated) {
@@ -89,7 +95,7 @@ class WebSocketDialog extends AbstractModel
}
$list = $builder
->orderByDesc('u.top_at')
->orderByDesc('web_socket_dialogs.last_at')
->orderByDesc('u.last_at')
->paginate(Base::getPaginate(100, 50));
$list->transform(function (WebSocketDialog $item) use ($userid) {
return $item->formatData($userid);
@@ -103,31 +109,70 @@ class WebSocketDialog extends AbstractModel
}
/**
* 获取未读对话列表
* 列表外的未读对话 和 列表外的待办对话
* @param $userid
* @param $beforeAt
* @param $take
* @param $unreadAt
* @param $todoAt
* @return WebSocketDialog[]
*/
public static function getDialogUnread($userid, $beforeAt, $take = 20)
public static function getDialogBeyond($userid, $unreadAt, $todoAt)
{
DB::statement("SET SQL_MODE=''");
$list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->join('web_socket_dialog_msg_reads as r', 'web_socket_dialogs.id', '=', 'r.dialog_id')
->where('u.userid', $userid)
->where('r.userid', $userid)
->where('r.silence', 0)
->where('r.read_at')
->where('web_socket_dialogs.last_at', '>', $beforeAt)
->groupBy('web_socket_dialogs.id')
->take(min(100, $take))
->get();
$list->transform(function (WebSocketDialog $item) use ($userid) {
return $item->formatData($userid);
});
//
return $list;
$ids = [];
$array = [];
if ($unreadAt) {
// 未读对话
$list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->join('web_socket_dialog_msg_reads as r', 'web_socket_dialogs.id', '=', 'r.dialog_id')
->where('u.userid', $userid)
->where('r.userid', $userid)
->where('r.read_at')
->where('u.last_at', '<', $unreadAt)
->groupBy('u.dialog_id')
->take(20)
->get();
$list->transform(function (WebSocketDialog $item) use ($userid, &$ids, &$array) {
if (!in_array($item->id, $ids)) {
$ids[] = $item->id;
$array[] = $item->formatData($userid);
}
});
// 标记未读会话
$list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->where('u.userid', $userid)
->where('u.mark_unread', 1)
->where('u.last_at', '<', $unreadAt)
->take(20)
->get();
$list->transform(function (WebSocketDialog $item) use ($userid, &$ids, &$array) {
if (!in_array($item->id, $ids)) {
$ids[] = $item->id;
$array[] = $item->formatData($userid);
}
});
}
if ($todoAt) {
// 待办会话
$list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->join('web_socket_dialog_msg_todos as t', 'web_socket_dialogs.id', '=', 't.dialog_id')
->where('u.userid', $userid)
->where('t.userid', $userid)
->where('t.done_at')
->where('u.last_at', '<', $todoAt)
->groupBy('u.dialog_id')
->take(20)
->get();
$list->transform(function (WebSocketDialog $item) use ($userid, &$ids, &$array) {
if (!in_array($item->id, $ids)) {
$ids[] = $item->id;
$array[] = $item->formatData($userid);
}
});
}
return $array;
}
@@ -147,14 +192,16 @@ class WebSocketDialog extends AbstractModel
};
//
$time = Carbon::parse($this->user_at ?? $dialogUserFun('updated_at'));
$this->hide = $this->hide ?? $dialogUserFun('hide');
$this->top_at = $this->top_at ?? $dialogUserFun('top_at');
$this->last_at = $this->last_at ?? $dialogUserFun('last_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;
$this->last_at = $this->last_msg ? Carbon::parse($this->last_msg->created_at)->format('Y-m-d H:i:s') : null;
} else {
// 未读信息
if (Base::judgeClientVersion("0.34.0")) {
@@ -197,9 +244,13 @@ class WebSocketDialog extends AbstractModel
$this->dialog_delete = 1;
}
$this->dialog_user = $dialog_user;
$this->dialog_mute = Base::settingFind('system', 'user_private_chat_mute');
break;
case "group":
switch ($this->group_type) {
case 'user':
$this->dialog_mute = Base::settingFind('system', 'user_group_chat_mute');
break;
case 'project':
$this->group_info = Project::withTrashed()->select(['id', 'name', 'archived_at', 'deleted_at'])->whereDialogId($this->id)->first()?->cancelAppend()->cancelHidden();
if ($this->group_info) {
@@ -220,7 +271,7 @@ class WebSocketDialog extends AbstractModel
break;
case 'all':
$this->name = Doo::translate('全体成员');
$this->all_group_mute = Base::settingFind('system', 'all_group_mute');
$this->dialog_mute = Base::settingFind('system', 'all_group_mute');
break;
}
break;
@@ -459,17 +510,37 @@ class WebSocketDialog extends AbstractModel
*/
public function checkMute($userid)
{
if ($this->group_type === 'all') {
$allGroupMute = Base::settingFind('system', 'all_group_mute');
switch ($allGroupMute) {
case 'all':
throw new ApiException('当前会话全员禁言');
case 'user':
if (!User::find($userid)?->isAdmin()) {
throw new ApiException('当前会话禁言');
$muteMsgTip = null;
$systemConfig = Base::setting('system');
switch ($this->type) {
case 'user':
if ($systemConfig['user_private_chat_mute'] === 'close') {
$muteMsgTip = '个人会话禁言';
}
break;
case 'group':
if ($this->group_type === 'user') {
if ($systemConfig['user_group_chat_mute'] === 'close') {
$muteMsgTip = '个人群组禁言';
}
} elseif ($this->group_type === 'all') {
if ($systemConfig['all_group_mute'] === 'close') {
$muteMsgTip = '当前会话全员禁言';
}
}
break;
}
if ($muteMsgTip === null) {
return;
}
if ($userid) {
$user = User::find($userid);
if ($user?->bot || $user?->isAdmin()) { // 机器人或管理员不受禁言
return;
}
}
throw new ApiException($muteMsgTip);
}
/**
@@ -527,20 +598,6 @@ 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
@@ -597,7 +654,6 @@ class WebSocketDialog extends AbstractModel
'name' => $name ?: '',
'group_type' => $group_type,
'owner_id' => $owner_id,
'last_at' => in_array($group_type, ['user', 'department', 'all']) ? Carbon::now() : null,
]);
$dialog->save();
foreach (is_array($userid) ? $userid : [$userid] as $value) {
@@ -605,7 +661,8 @@ class WebSocketDialog extends AbstractModel
WebSocketDialogUser::createInstance([
'dialog_id' => $dialog->id,
'userid' => $value,
'important' => !in_array($group_type, ['user', 'all'])
'important' => !in_array($group_type, ['user', 'all']),
'last_at' => in_array($group_type, ['user', 'department', 'all']) ? Carbon::now() : null,
])->save();
}
}

View File

@@ -33,18 +33,21 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int|null $reply_id 回复ID
* @property int|null $forward_id 转发ID
* @property int|null $forward_num 被转发多少次
* @property int|null $forward_show 是否显示转发的来源
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property-read \App\Models\WebSocketDialogMsg|null $forward_data
* @property-read int|mixed $percentage
* @property-read \App\Models\WebSocketDialogMsg|null $reply_data
* @property-read \App\Models\WebSocketDialog|null $webSocketDialog
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereDeletedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereDialogId($value)
@@ -52,7 +55,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereEmoji($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereForwardId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereForwardNum($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereForwardShow($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereKey($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereLink($value)
@@ -78,8 +80,6 @@ class WebSocketDialogMsg extends AbstractModel
protected $appends = [
'percentage',
'reply_data',
'forward_data',
];
protected $hidden = [
@@ -107,36 +107,6 @@ class WebSocketDialogMsg extends AbstractModel
return $this->appendattrs['percentage'];
}
/**
* 回复消息详情
* @return WebSocketDialogMsg|null
*/
public function getReplyDataAttribute()
{
if (!isset($this->appendattrs['reply_data'])) {
$this->appendattrs['reply_data'] = null;
if ($this->reply_id > 0) {
$this->appendattrs['reply_data'] = self::find($this->reply_id, ['id', 'userid', 'type', 'msg'])?->cancelAppend() ?: null;
}
}
return $this->appendattrs['reply_data'];
}
/**
* 转发消息详情
* @return WebSocketDialogMsg|null
*/
public function getForwardDataAttribute()
{
if (!isset($this->appendattrs['forward_data'])) {
$this->appendattrs['forward_data'] = null;
if ($this->forward_id > 0) {
$this->appendattrs['forward_data'] = self::find($this->forward_id, ['id', 'userid', 'type', 'msg'])?->cancelAppend() ?: null;
}
}
return $this->appendattrs['forward_data'];
}
/**
* 消息格式化
* @param $value
@@ -147,13 +117,9 @@ class WebSocketDialogMsg extends AbstractModel
if (is_array($value)) {
return $value;
}
$value = Base::json2array($value);
if ($this->type === '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') {
$value['path'] = Base::fillUrl($value['path']);
$value = $this->formatDataMsg($this->type, $value);
if (isset($value['reply_data'])) {
$value['reply_data']['msg'] = $this->formatDataMsg($value['reply_data']['type'], $value['reply_data']['msg']);
}
return $value;
}
@@ -171,6 +137,27 @@ class WebSocketDialogMsg extends AbstractModel
return Base::json2array($value);
}
/**
* 处理消息数据
* @param $type
* @param $msg
* @return mixed
*/
private function formatDataMsg($type, $msg)
{
if (!is_array($msg)) {
$msg = Base::json2array($msg);
}
if ($type === 'file') {
$msg['type'] = in_array($msg['ext'], ['jpg', 'jpeg', 'webp', 'png', 'gif']) ? 'img' : 'file';
$msg['path'] = Base::fillUrl($msg['path']);
$msg['thumb'] = Base::fillUrl($msg['thumb'] ?: Base::extIcon($msg['ext']));
} else if ($type === 'record') {
$msg['path'] = Base::fillUrl($msg['path']);
}
return $msg;
}
/**
* 获取占比
* @param bool|int $increment 是否新增阅读数
@@ -391,15 +378,25 @@ class WebSocketDialogMsg extends AbstractModel
* 转发消息
* @param array|int $dialogids
* @param array|int $userids
* @param User $user 发送的会员
* @param int $showSource 是否显示原发送者信息
* @param string $leaveMessage 转发留言
* @param User $user 发送的会员
* @param int $showSource 是否显示原发送者信息
* @param string $leaveMessage 转发留言
* @return mixed
*/
public function forwardMsg($dialogids, $userids, $user, $showSource = 1, $leaveMessage = '')
{
return AbstractModel::transaction(function() use ($dialogids, $user, $userids, $showSource, $leaveMessage) {
$originalMsg = Base::json2array($this->getRawOriginal('msg'));
return AbstractModel::transaction(function () use ($dialogids, $user, $userids, $showSource, $leaveMessage) {
$msgData = Base::json2array($this->getRawOriginal('msg'));
$forwardData = is_array($msgData['forward_data']) ? $msgData['forward_data'] : [];
$forwardId = $forwardData['id'] ?: $this->id;
$forwardUserid = $forwardData['userid'] ?: $this->userid;
$msgData['forward_data'] = [
'id' => $forwardId, // 转发的消息ID原始
'userid' => $forwardUserid, // 转发的消息会员ID原始
'parent_id' => $this->id, // 转发的消息ID
'parent_userid' => $this->userid, // 转发的消息会员ID
'show' => $showSource, // 是否显示原发送者信息
];
$msgs = [];
$already = [];
if ($dialogids) {
@@ -407,7 +404,7 @@ class WebSocketDialogMsg extends AbstractModel
$dialogids = [$dialogids];
}
foreach ($dialogids as $dialogid) {
$res = self::sendMsg('forward-'.( $showSource ? 1 : 0).'-'.($this->forward_id ?: $this->id), $dialogid, $this->type, $originalMsg, $user->userid);
$res = self::sendMsg('forward-' . $forwardId, $dialogid, $this->type, $msgData, $user->userid);
if (Base::isSuccess($res)) {
$msgs[] = $res['data'];
$already[] = $dialogid;
@@ -427,7 +424,7 @@ class WebSocketDialogMsg extends AbstractModel
}
$dialog = WebSocketDialog::checkUserDialog($user, $userid);
if ($dialog && !in_array($dialog->id, $already)) {
$res = self::sendMsg('forward-'.( $showSource ? 1 : 0).'-'.($this->forward_id ?: $this->id), $dialog->id, $this->type, $originalMsg, $user->userid);
$res = self::sendMsg('forward-' . $forwardId, $dialog->id, $this->type, $msgData, $user->userid);
if (Base::isSuccess($res)) {
$msgs[] = $res['data'];
}
@@ -437,6 +434,9 @@ class WebSocketDialogMsg extends AbstractModel
}
}
}
if (count($msgs) > 0) {
$this->increment('forward_num', count($msgs));
}
return Base::retSuccess('转发成功', [
'msgs' => $msgs
]);
@@ -458,9 +458,8 @@ class WebSocketDialogMsg extends AbstractModel
WebSocketDialogMsgTodo::whereIn('msg_id', $ids)->delete();
self::whereIn('id', $ids)->delete();
//
$dialogDatas = WebSocketDialog::whereIn('id', $dialogIds)->get();
foreach ($dialogDatas as $dialogData) {
$dialogData->updateMsgLastAt();
foreach ($dialogIds as $dialogId) {
WebSocketDialogUser::updateMsgLastAt($dialogId);
}
foreach ($replyIds as $id) {
self::whereId($id)->update(['reply_num' => self::whereReplyId($id)->count()]);
@@ -501,7 +500,7 @@ class WebSocketDialogMsg extends AbstractModel
'data' => [
'id' => $this->id,
'dialog_id' => $this->dialog_id,
'last_msg' => $dialogData->updateMsgLastAt(),
'last_msg' => WebSocketDialogUser::updateMsgLastAt($this->dialog_id),
'update_read' => $deleteRead ? 1 : 0
],
]
@@ -786,7 +785,20 @@ 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_callback("/\<(blockquote|strong|pre|ol|ul|li|em|p|s|u)(.*?)\>/is", function (array $match) { // 不用去除a标签上面已经处理过了
preg_match("/<[^>]*?style=([\"'])(.*?)\\1[^>]*?>/is", $match[0], $matchs);
$attach = '';
if ($matchs) {
$styleArray = explode(';', $matchs[2]);
$validStyles = array_filter($styleArray, function ($styleItem) {
return preg_match('/\s*(?:color|font-size|background-color|font-weight|font-family|text-decoration|font-style)\s*:/i', $styleItem); // 只保留指定样式
});
if ($validStyles) {
$attach = ' style="' . implode(';', $validStyles) . '"';
}
}
return "<{$match[1]}{$attach}>";
}, $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);
@@ -846,10 +858,10 @@ class WebSocketDialogMsg extends AbstractModel
$push_silence = !in_array($type, ["text", "file", "record", "meeting"]);
}
//
$update_id = preg_match("/^update-(\d+)$/", $action, $match) ? $match[1] : 0;
$change_id = preg_match("/^change-(\d+)$/", $action, $match) ? $match[1] : 0;
$reply_id = preg_match("/^reply-(\d+)$/", $action, $match) ? $match[1] : 0;
$forward_id = preg_match("/^forward-(\d+)-(\d+)$/", $action, $match) ? $match[2] : 0;
$update_id = intval(preg_match("/^update-(\d+)$/", $action, $match) ? $match[1] : 0);
$change_id = intval(preg_match("/^change-(\d+)$/", $action, $match) ? $match[1] : 0);
$reply_id = intval(preg_match("/^reply-(\d+)$/", $action, $match) ? $match[1] : 0);
$forward_id = intval(preg_match("/^forward-(\d+)$/", $action, $match) ? $match[1] : 0);
$sender = $sender === null ? User::userid() : $sender;
//
$dialog = WebSocketDialog::find($dialog_id);
@@ -871,23 +883,37 @@ class WebSocketDialogMsg extends AbstractModel
if (empty($dialogMsg)) {
throw new ApiException('消息不存在');
}
if ($dialogMsg->type !== 'text' && $dialogMsg->type !== 'vote') {
throw new ApiException('此消息不支持此操作');
}
if ($dialogMsg->userid != $sender && $dialogMsg->type !== 'vote') {
throw new ApiException('仅支持修改自己的消息');
$oldMsg = Base::json2array($dialogMsg->getRawOriginal('msg'));
if ($dialogMsg->type === 'vote') {
if ($dialogMsg->userid != $sender) {
$msg = [
'votes' => $msg['votes'],
];
}
} else {
if ($dialogMsg->type !== 'text') {
throw new ApiException('此消息不支持此操作');
}
if ($dialogMsg->userid != $sender) {
throw new ApiException('仅支持修改自己的消息');
}
}
//
$updateData = [
'mtype' => $mtype,
'link' => $link,
'msg' => $msg,
'msg' => array_merge($oldMsg, $msg),
'modify' => $modify,
];
$dialogMsg->updateInstance($updateData);
$dialogMsg->key = $dialogMsg->generateMsgKey();
$dialogMsg->save();
//
WebSocketDialogUser::whereDialogId($dialog->id)->whereUserid($sender)->whereHide(1)->change([
'hide' => 0, // 修改消息时,显示会话(仅自己)
'updated_at' => Carbon::now()->toDateTimeString('millisecond'),
]);
//
$dialogMsg->msgJoinGroup($dialog);
//
$dialog->pushMsg('update', array_merge($updateData, [
@@ -897,34 +923,53 @@ class WebSocketDialogMsg extends AbstractModel
return Base::retSuccess('修改成功', $dialogMsg);
} else {
// 发送
if ($reply_id && !self::whereId($reply_id)->increment('reply_num')) {
throw new ApiException('回复的消息不存在');
}
// 转发
if ($forward_id && !self::whereId($forward_id)->increment('forward_num')) {
throw new ApiException('转发的消息不存在');
if ($reply_id) {
// 回复
$replyRow = self::whereId($reply_id)->whereDialogId($dialog_id)->first();
if (empty($replyRow)) {
throw new ApiException('回复的消息不存在');
}
$replyMsg = Base::json2array($replyRow->getRawOriginal('msg'));
unset($replyMsg['reply_data']);
$msg['reply_data'] = [
'id' => $replyRow->id,
'userid' => $replyRow->userid,
'type' => $replyRow->type,
'msg' => $replyMsg,
];
$replyRow->increment('reply_num');
}
//
$dialogMsg = self::createInstance([
'dialog_id' => $dialog_id,
'dialog_type' => $dialog->type,
'reply_id' => $reply_id,
'forward_id' => $forward_id,
'userid' => $sender,
'type' => $type,
'mtype' => $mtype,
'link' => $link,
'msg' => $msg,
'read' => 0,
'forward_id' => $forward_id,
'forward_show' => $forward_id ? $match[1] : 1,
]);
AbstractModel::transaction(function () use ($dialog, $dialogMsg) {
$dialog->last_at = Carbon::now();
$dialog->save();
AbstractModel::transaction(function () use ($dialogMsg) {
$dialogMsg->send = 1;
$dialogMsg->key = $dialogMsg->generateMsgKey();
$dialogMsg->save();
WebSocketDialogUser::whereDialogId($dialog->id)->change(['updated_at' => Carbon::now()->toDateTimeString('millisecond')]);
//
if ($dialogMsg->type === 'meeting') {
MeetingMsg::createInstance([
'meetingid' => $dialogMsg->msg['meetingid'],
'dialog_id' => $dialogMsg->dialog_id,
'msg_id' => $dialogMsg->id,
])->save();
}
//
WebSocketDialogUser::whereDialogId($dialogMsg->dialog_id)->change([
'hide' => 0, // 有新消息时,显示会话(会话内所有会员)
'last_at' => Carbon::now(),
'updated_at' => Carbon::now()->toDateTimeString('millisecond'),
]);
});
//
$task = new WebSocketDialogMsgTask($dialogMsg->id);

View File

@@ -17,9 +17,15 @@ use Carbon\Carbon;
* @property int|null $after 在阅读之后才添加的记录
* @property string|null $read_at 阅读时间
* @property-read \App\Models\WebSocketDialogMsg|null $webSocketDialogMsg
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereAfter($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereEmail($value)

View File

@@ -11,9 +11,15 @@ namespace App\Models;
* @property int|null $userid 接收会员ID
* @property string|null $done_at 完成时间
* @property-read array|mixed $msg_data
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgTodo newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgTodo newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgTodo query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgTodo whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgTodo whereDoneAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgTodo whereId($value)

View File

@@ -11,23 +11,33 @@ use Carbon\Carbon;
* @property int|null $dialog_id 对话ID
* @property int|null $userid 会员ID
* @property string|null $top_at 置顶时间
* @property string|null $last_at 最后消息时间
* @property int|null $mark_unread 是否标记为未读0否1是
* @property int|null $silence 是否免打扰0否1是
* @property int|null $hide 不显示会话0否1是
* @property int|null $inviter 邀请人
* @property int|null $important 是否不可移出(项目、任务人员)
* @property int|null $important 是否不可移出(项目、任务、部门人员)
* @property string|null $color 颜色
* @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|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereColor($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereHide($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereImportant($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereInviter($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereLastAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereMarkUnread($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereSilence($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereTopAt($value)
@@ -46,4 +56,17 @@ class WebSocketDialogUser extends AbstractModel
{
return $this->hasOne(WebSocketDialog::class, 'id', 'dialog_id');
}
/**
* 更新对话最后消息时间
* @return WebSocketDialogMsg|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|\Illuminate\Database\Query\Builder|object|null
*/
public static function updateMsgLastAt($dialogId)
{
$lastMsg = WebSocketDialogMsg::whereDialogId($dialogId)->orderByDesc('id')->first();
if ($lastMsg) {
WebSocketDialogUser::whereDialogId($dialogId)->change(['last_at' => $lastMsg->created_at]);
}
return $lastMsg;
}
}

View File

@@ -12,9 +12,15 @@ namespace App\Models;
* @property int|null $create_id 所属会员ID
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketTmpMsg newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketTmpMsg newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketTmpMsg query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketTmpMsg whereCreateId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketTmpMsg whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketTmpMsg whereId($value)

View File

@@ -133,7 +133,8 @@ class Base
/**
* 判断客户端版本
* @param $min
* @param $min // 最小版本满足此版本返回true
* @param null $clientVersion
* @return bool
*/
public static function judgeClientVersion($min, $clientVersion = null)
@@ -2472,13 +2473,18 @@ class Base
*/
public static function extIcon($ext)
{
return match ($ext) {
"docx" => 'images/ext/doc.png',
"xlsx" => 'images/ext/xls.png',
"pptx" => 'images/ext/ppt.png',
"ai", "avi", "bmp", "cdr", "doc", "eps", "gif", "mov", "mp3", "mp4", "pdf", "ppt", "pr", "psd", "rar", "svg", "tif", "txt", "xls", "zip" => 'images/ext/' . $ext . '.png',
default => 'images/ext/file.png',
};
if ($ext == "docx") {
$ext = 'doc';
} elseif ($ext == "xlsx") {
$ext = 'xls';
} elseif ($ext == "pptx") {
$ext = 'ppt';
}
if (in_array($ext, ["ai", "avi", "bmp", "cdr", "doc", "eps", "gif", "mov", "mp3", "mp4", "pdf", "ppt", "pr", "psd", "rar", "svg", "tif", "txt", "xls", "zip"])) {
return 'images/ext/' . $ext . '.png';
} else {
return 'images/ext/file.png';
}
}
/**

View File

@@ -228,6 +228,14 @@ class Doo
if (Base::isError($data)) {
throw new ApiException($data['msg'] ?: '注册失败');
}
if (\DB::transactionLevel() > 0) {
try {
\DB::commit();
\DB::beginTransaction();
} catch (\Throwable) {
// do nothing
}
}
$user = User::whereEmail($email)->first();
if (empty($user)) {
throw new ApiException('注册失败');

View File

@@ -174,19 +174,16 @@ class Extranet
/**
* 随机笑话接口
* @return array
* @return string
*/
public static function randJoke(): array
public static function randJoke(): string
{
$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'];
}
$data = self::curl("https://hmajax.itheima.net/api/randjoke");
$data = Base::json2array($data);
if ($data['message'] === '获取成功' && $text = trim($data['data'])) {
return $text;
}
return [];
return "";
}
/**
@@ -195,13 +192,10 @@ class Extranet
*/
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;
}
$data = self::curl("https://hmajax.itheima.net/api/ambition");
$data = Base::json2array($data);
if ($data['message'] === '获取成功' && $text = trim($data['data'])) {
return $text;
}
return "";
}

View File

@@ -29,8 +29,9 @@ class TimeRange
$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;
$timezone = config('app.timezone');
$this->updated = $updated ? Carbon::parse($updated)->setTimezone($timezone) : null;
$this->deleted = $deleted ? Carbon::parse($deleted)->setTimezone($timezone) : null;
}
/**

View File

@@ -4,6 +4,7 @@ namespace App\Observers;
use App\Models\Deleted;
use App\Models\WebSocketDialogUser;
use Carbon\Carbon;
class WebSocketDialogUserObserver
{
@@ -15,6 +16,18 @@ class WebSocketDialogUserObserver
*/
public function created(WebSocketDialogUser $webSocketDialogUser)
{
if (!$webSocketDialogUser->last_at) {
if (in_array($webSocketDialogUser->webSocketDialog?->group_type, ['user', 'department', 'all'])) {
$webSocketDialogUser->last_at = Carbon::now();
$webSocketDialogUser->save();
} else {
$item = WebSocketDialogUser::whereDialogId($webSocketDialogUser->dialog_id)->orderByDesc('last_at')->first();
if ($item?->last_at) {
$webSocketDialogUser->last_at = $item->last_at;
$webSocketDialogUser->save();
}
}
}
Deleted::forget('dialog', $webSocketDialogUser->dialog_id, $webSocketDialogUser->userid);
}

View File

@@ -62,6 +62,7 @@ class WebSocketService implements WebSocketHandlerInterface
'type' => 'open',
'data' => [
'fd' => $fd,
'ud' => $user->userid,
],
]));
// 通知上线

View File

@@ -316,12 +316,12 @@ class BotReceiveMsgTask extends AbstractTask
$nameKey = $isManager ? $array[2] : $array[1];
$data = $this->botManagerOne($botId, $msg->userid);
if ($data) {
$list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.color', 'u.updated_at as user_at'])
$list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->where('web_socket_dialogs.name', 'LIKE', "%{$nameKey}%")
->where('u.userid', $data->userid)
->orderByDesc('u.top_at')
->orderByDesc('web_socket_dialogs.last_at')
->orderByDesc('u.last_at')
->take(20)
->get();
if ($list->isEmpty()) {
@@ -438,6 +438,24 @@ class BotReceiveMsgTask extends AbstractTask
$error = 'The client version is low (required version ≥ v0.29.12).';
}
break;
// Gemini 机器人
case 'ai-gemini@bot.system':
$setting = Base::setting('aibotSetting');
$webhookUrl = "{$serverUrl}/ai/gemini/send";
$extras = [
'gemini_key' => $setting['gemini_key'],
'gemini_model' => $setting['gemini_model'],
'gemini_agency' => $setting['gemini_agency'],
'gemini_timeout' => 20,
'server_url' => $serverUrl,
];
if (empty($extras['gemini_key'])) {
$error = 'Robot disabled.';
} elseif (in_array($this->client['platform'], ['win', 'mac', 'web'])
&& !Base::judgeClientVersion("0.29.11", $this->client['version'])) {
$error = 'The client version is low (required version ≥ v0.29.12).';
}
break;
// 其他机器人
default:
$userBot = UserBot::whereBotId($botUser->userid)->first();
@@ -453,7 +471,7 @@ class BotReceiveMsgTask extends AbstractTask
}
//
try {
$res = Ihttp::ihttp_post($webhookUrl, [
$data = [
'text' => $command,
'token' => User::generateToken($botUser),
'dialog_id' => $dialog->id,
@@ -464,7 +482,8 @@ class BotReceiveMsgTask extends AbstractTask
'bot_uid' => $botUser->userid,
'version' => Base::getVersion(),
'extras' => Base::array2json($extras)
], 10);
];
$res = Ihttp::ihttp_post($webhookUrl, $data);
if ($userBot) {
$userBot->webhook_num++;
$userBot->save();

View File

@@ -0,0 +1,132 @@
<?php
namespace App\Tasks;
use App\Models\Meeting;
use App\Models\WebSocketDialog;
use App\Module\Base;
use Carbon\Carbon;
use App\Models\WebSocketDialogMsg;
use Illuminate\Support\Facades\Cache;
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
class CloseMeetingRoomTask extends AbstractTask
{
public function __construct()
{
parent::__construct();
}
public function start()
{
// 10分钟执行一次
$time = intval(Cache::get("CloseMeetingRoomTask:Time"));
if (time() - $time < 600) {
return;
}
Cache::put("CloseMeetingRoomTask:Time", time(), Carbon::now()->addMinutes(10));
// 判断参数
$setting = Base::setting('meetingSetting');
if ($setting['open'] !== 'open') {
return;
}
if (empty($setting['appid']) ||empty($setting['api_key']) || empty($setting['api_secret'])) {
return;
}
$credentials = $setting['api_key'] . ":" . $setting['api_secret'];
$base64Credentials = base64_encode($credentials);
$arrHeader = [
"Accept: application/json",
"Authorization: Basic " . $base64Credentials
];
// 获取10分钟未更新的会议
$meetings = Meeting::whereNull('end_at')
->where('updated_at', '<', Carbon::now()->subMinutes(10))
->take(100)
->get();
$dialogIds = [];
/** @var Meeting $meeting */
foreach ($meetings as $meeting) {
if (!$this->isEmptyChannel($setting['appid'], $meeting->channel, $arrHeader)) {
$meeting->updated_at = Carbon::now();
$meeting->save();
continue;
}
$meeting->end_at = Carbon::now();
$meeting->save();
// 更新消息
$newMsg = $meeting->toArray();
$newMsg['end_at'] = $meeting->end_at->toDateTimeString();
WebSocketDialogMsg::select(['web_socket_dialog_msgs.*', 'm.meetingid'])
->join("meeting_msgs as m", "m.msg_id", "=", "web_socket_dialog_msgs.id")
->where('m.meetingid', $meeting->meetingid)
->chunk(100, function ($msgs) use ($newMsg, &$dialogIds) {
/** @var WebSocketDialogMsg $msg */
foreach ($msgs as $msg) {
$msgData = Base::json2array($msg->getRawOriginal('msg'));
$msg->msg = Base::array2json(array_merge($msgData, $newMsg));
$msg->save();
//
if (!isset($dialogIds[$msg->dialog_id])) {
$dialogIds[$msg->dialog_id] = [];
}
$dialogIds[$msg->dialog_id][] = [
'id' => $msg->id,
'msg' => $msg->msg,
];
}
});
}
// 推送更新
foreach ($dialogIds as $dialogId => $datas) {
$dialog = WebSocketDialog::find($dialogId);
if (empty($dialog)) {
continue;
}
foreach ($datas as $data) {
$dialog->pushMsg('update', $data);
}
}
}
public function end()
{
}
/**
* 是否空频道
* @param $appid
* @param $channel
* @param $arrHeader
* @return bool
*/
private function isEmptyChannel($appid, $channel, $arrHeader)
{
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.sd-rtn.com/dev/v1/channel/user/{$appid}/{$channel}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => $arrHeader,
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
return false; // 错误
}
$data = Base::json2array($response);
if (!$data['success']) {
return false; // 失败
}
if ($data['data']['channel_exist']) {
return false; // 有人
}
return true;
}
}

View File

@@ -13,13 +13,15 @@ use Carbon\Carbon;
/**
* 获取笑话、心灵鸡汤
*
* 在.env添加笑话 JUKE_KEY_JOKE
* 在.env添加鸡汤 JUKE_KEY_SOUP
*
* 每日小时采集1次
* 每分钟采集1次
*/
class JokeSoupTask extends AbstractTask
{
public static function keyName($key)
{
return "JokeSoupTask-v2:{$key}";
}
public function __construct()
{
parent::__construct();
@@ -27,27 +29,25 @@ class JokeSoupTask extends AbstractTask
public function start()
{
// 判断每小时执行一次
if (Cache::get("JokeSoupTask:YmdH") == date("YmdH")) {
// 判断每分钟执行一次
if (Cache::get(self::keyName("YmdHi")) == date("YmdHi")) {
return;
}
Cache::put("JokeSoupTask:YmdH", date("YmdH"), Carbon::now()->addDay());
Cache::put(self::keyName("YmdHi"), date("YmdHi"), Carbon::now()->addDay());
//
$array = Base::json2array(Cache::get("JokeSoupTask:jokes"));
$array = Base::json2array(Cache::get(self::keyName("jokes")));
$data = Extranet::randJoke();
foreach ($data as $item) {
if ($text = trim($item['content'])) {
$array[] = $text;
}
if ($data) {
$array[] = $data;
}
Cache::forever("JokeSoupTask:jokes", Base::array2json(array_slice($array, -100)));
Cache::forever(self::keyName("jokes"), Base::array2json(array_slice($array, -200)));
//
$array = Base::json2array(Cache::get("JokeSoupTask:soups"));
$array = Base::json2array(Cache::get(self::keyName("soups")));
$data = Extranet::soups();
if ($data) {
$array[] = $data;
}
Cache::forever("JokeSoupTask:soups", Base::array2json(array_slice($array, -24)));
Cache::forever(self::keyName("soups"), Base::array2json(array_slice($array, -200)));
}
public function end()

169
bin/https Normal file
View File

@@ -0,0 +1,169 @@
#!/bin/sh
#fonts color
Green="\033[32m"
Yellow="\033[33m"
Red="\033[31m"
GreenBG="\033[42;37m"
YellowBG="\033[43;37m"
RedBG="\033[41;37m"
Font="\033[0m"
#notification information
OK="${Green}[OK]${Font}"
Warn="${Yellow}[警告]${Font}"
Error="${Red}[错误]${Font}"
cd "$(
cd "$(dirname "$0")" || exit
pwd
)" || exit
#================================================================
#================================================================
success() {
echo -e "${OK} ${GreenBG}$1${Font}"
}
warning() {
echo -e "${Warn} ${YellowBG}$1${Font}"
}
error() {
echo -e "${Error} ${RedBG}$1${Font}"
}
info() {
echo -e "$1"
}
env_get() {
local key=$1
local value=`cat $(dirname "$PWD")/.env | grep "^$key=" | awk -F '=' '{print $2}'`
echo "$value"
}
env_set() {
local key=$1
local val=$2
local exist=`cat $(dirname "$PWD")/.env | grep "^$key="`
if [ -z "$exist" ]; then
echo "$key=$val" >> $(dirname "$PWD")/.env
else
sed -i "/^${key}=/c\\${key}=${val}" $(dirname "$PWD")/.env
fi
}
#================================================================
#================================================================
check() {
while [ -z "$domain" ]; do
read -rp "请输入你的域名: " domain
# 判断域名是否合法
if [ -z "$domain" ]; then
error "域名不能为空"
elif [ -z "$(echo "$domain" | grep -E '^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$')" ]; then
error "域名格式不正确"
domain=""
fi
done
success "正在域名DNS解析IP..."
local domain_ip=$(ping -c 1 "${domain}" 2>/dev/null | grep PING | cut -d "(" -f2 | cut -d ")" -f1)
local local_ip=$(curl -sk ip.sb)
info "域名DNS解析IP: ${domain_ip}"
info "本机IP: ${local_ip}"
sleep 2
if [[ "$(echo "${local_ip}" | tr '.' '+' | bc)" == "$(echo "${domain_ip}" | tr '.' '+' | bc)" ]]; then
success "域名DNS解析IP 与 本机IP 匹配"
sleep 2
else
warning "域名DNS解析IP 与 本机IP 不匹配,是否继续操作? [Y/n]"
read -r continue_next
[[ -z ${continue_next} ]] && continue_next="Y"
case $continue_next in
[yY][eE][sS] | [yY])
success "继续操作"
sleep 2
;;
*)
error "操作终止"
exit 2
;;
esac
fi
}
install() {
local sitePath="$(dirname "$PWD")/docker/nginx/site"
local sslPath="$sitePath/ssl"
if [[ -f "$sslPath/$domain.key" && -f "$sslPath/$domain.crt" ]]; then
warning "$domain 证书文件已存在,是否删除并继续操作? [Y/n]"
read -r continue_install
[[ -z ${continue_install} ]] && continue_install="Y"
case $continue_install in
[yY][eE][sS] | [yY])
rm -f "$sslPath/$domain.key"
rm -f "$sslPath/$domain.crt"
success "继续操作"
sleep 2
;;
*)
error "操作终止"
exit 2
;;
esac
fi
apk add --no-cache openssl socat
curl https://get.acme.sh | sh
if [[ 0 -ne $? ]]; then
error "安装证书生成脚本失败"
exit 1
fi
if /root/.acme.sh/acme.sh --issue -d "${domain}" -w "$(dirname "$PWD")/public" --standalone -k ec-256 --force --test; then
success "SSL 证书测试签发成功,开始正式签发"
rm -rf "/root/.acme.sh/${domain}_ecc"
sleep 2
else
error "SSL 证书测试签发失败"
rm -rf "root/.acme.sh/${domain}_ecc"
exit 1
fi
if /root/.acme.sh/acme.sh --issue -d "${domain}" -w "$(dirname "$PWD")/public" --server letsencrypt --standalone -k ec-256 --force; then
success "SSL 证书生成成功"
sleep 2
mkdir -p $sslPath
if /root/.acme.sh/acme.sh --installcert -d "${domain}" --fullchainpath "${sslPath}/${domain}.crt" --keypath "${sslPath}/${domain}.key" --ecc --force; then
success "SSL 证书配置成功"
sleep 2
fi
else
error "SSL 证书生成失败"
rm -rf "/root/.acme.sh/${domain}_ecc"
exit 1
fi
env_set "APP_URL" "https://${domain}"
cat >${sitePath}/ssl.conf <<EOF
server_name ${domain};
listen 443 ssl;
ssl_certificate /etc/nginx/conf.d/site/ssl/${domain}.crt;
ssl_certificate_key /etc/nginx/conf.d/site/ssl/${domain}.key;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://\$host\$request_uri;
EOF
}
check
install

View File

@@ -43,7 +43,7 @@ runExec("git rev-list --count HEAD $(git branch | sed -n -e 's/^\* \(.*\)/\1/p')
console.error(err);
return;
}
const num = 1238 + parseInt(response)
const num = 1250 + parseInt(response)
if (isNaN(num) || Math.floor(num % 100) < 0) {
console.error("get version error " + response);
return;

148
cmd
View File

@@ -2,13 +2,16 @@
#fonts color
Green="\033[32m"
Yellow="\033[33m"
Red="\033[31m"
GreenBG="\033[42;37m"
YellowBG="\033[43;37m"
RedBG="\033[41;37m"
Font="\033[0m"
#notification information
OK="${Green}[OK]${Font}"
Warn="${Yellow}[警告]${Font}"
Error="${Red}[错误]${Font}"
cur_path="$(pwd)"
@@ -17,14 +20,30 @@ COMPOSE="docker-compose"
judge() {
if [[ 0 -eq $? ]]; then
echo -e "${OK} ${GreenBG} $1 完成 ${Font}"
success "$1 完成"
sleep 1
else
echo -e "${Error} ${RedBG} $1 失败${Font}"
error "$1 失败"
exit 1
fi
}
success() {
echo -e "${OK} ${GreenBG}$1${Font}"
}
warning() {
echo -e "${Warn} ${YellowBG}$1${Font}"
}
error() {
echo -e "${Error} ${RedBG}$1${Font}"
}
info() {
echo -e "$1"
}
rand() {
local min=$1
local max=$(($2-$min+1))
@@ -51,7 +70,7 @@ restart_php() {
$COMPOSE stop php
$COMPOSE start php
else
echo -e "$RES"
info "$RES"
fi
}
@@ -69,21 +88,21 @@ switch_debug() {
check_docker() {
docker --version &> /dev/null
if [ $? -ne 0 ]; then
echo -e "${Error} ${RedBG} 未安装 Docker${Font}"
error "未安装 Docker"
exit 1
fi
docker-compose version &> /dev/null
if [ $? -ne 0 ]; then
docker compose version &> /dev/null
if [ $? -ne 0 ]; then
echo -e "${Error} ${RedBG} 未安装 Docker-compose${Font}"
error "未安装 Docker-compose"
exit 1
fi
COMPOSE="docker compose"
fi
if [[ -n `$COMPOSE version | grep -E "\sv*1"` ]]; then
if [[ -n `$COMPOSE version | grep -E "\sv1"` ]]; then
$COMPOSE version
echo -e "${Error} ${RedBG} Docker-compose 版本过低请升级至v2+${Font}"
error "Docker-compose 版本过低请升级至v2+"
exit 1
fi
}
@@ -91,7 +110,17 @@ check_docker() {
check_node() {
npm --version &> /dev/null
if [ $? -ne 0 ]; then
echo -e "${Error} ${RedBG} 未安装nodejs${Font}"
error "未安装 npm"
exit 1
fi
node --version &> /dev/null
if [ $? -ne 0 ]; then
error "未安装 Node.js"
exit 1
fi
if [[ -n `node --version | grep -E "v1"` ]]; then
node --version
error "Node.js 版本过低请升级至v20+"
exit 1
fi
}
@@ -154,7 +183,7 @@ run_exec() {
local cmd=$2
local name=`docker_name $container`
if [ -z "$name" ]; then
echo -e "${Error} ${RedBG} 没有找到 $container 容器! ${Font}"
error "没有找到 $container 容器!"
exit 1
fi
docker exec -it "$name" /bin/sh -c "$cmd"
@@ -170,7 +199,7 @@ run_mysql() {
filename="${cur_path}/docker/mysql/backup/${database}_$(date "+%Y%m%d%H%M%S").sql.gz"
run_exec mariadb "exec mysqldump --databases $database -u$username -p$password" | gzip > $filename
judge "备份数据库"
[ -f "$filename" ] && echo -e "备份文件:$filename"
[ -f "$filename" ] && info "备份文件:$filename"
elif [ "$1" = "recovery" ]; then
database=$(env_get DB_DATABASE)
username=$(env_get DB_USERNAME)
@@ -179,19 +208,19 @@ run_mysql() {
mkdir -p ${cur_path}/docker/mysql/backup
list=`ls -1 "${cur_path}/docker/mysql/backup" | grep ".sql.gz"`
if [ -z "$list" ]; then
echo -e "${Error} ${RedBG} 没有备份文件!${Font}"
error "没有备份文件!"
exit 1
fi
echo "$list"
read -rp "请输入备份文件名称还原:" inputname
filename="${cur_path}/docker/mysql/backup/${inputname}"
if [ ! -f "$filename" ]; then
echo -e "${Error} ${RedBG} 备份文件:${inputname} 不存在! ${Font}"
error "备份文件:${inputname} 不存在!"
exit 1
fi
container_name=`docker_name mariadb`
if [ -z "$container_name" ]; then
echo -e "${Error} ${RedBG} 没有找到 mariadb 容器! ${Font}"
error "没有找到 mariadb 容器!"
exit 1
fi
docker cp $filename $container_name:/
@@ -201,6 +230,49 @@ run_mysql() {
fi
}
https_auto() {
restart_nginx="n"
if [[ "$(env_get APP_PORT)" != "80" ]]; then
warning "HTTP服务端口不是80是否修改并继续操作 [Y/n]"
read -r continue_http
[[ -z ${continue_http} ]] && continue_http="Y"
case $continue_http in
[yY][eE][sS] | [yY])
success "继续操作"
env_set "APP_PORT" "80"
restart_nginx="y"
;;
*)
error "操作终止"
exit 1
;;
esac
fi
if [[ "$(env_get APP_SSL_PORT)" != "443" ]]; then
warning "HTTPS服务端口不是443是否修改并继续操作 [Y/n]"
read -r continue_https
[[ -z ${continue_https} ]] && continue_https="Y"
case $continue_https in
[yY][eE][sS] | [yY])
success "继续操作"
env_set "APP_SSL_PORT" "443"
restart_nginx="y"
;;
*)
error "操作终止"
exit 1
;;
esac
fi
if [[ "$restart_nginx" == "y" ]]; then
$COMPOSE up -d
fi
docker run -it --rm -v $(pwd):/work nginx:alpine sh "/work/bin/https"
if [[ 0 -eq $? ]]; then
run_exec nginx "nginx -s reload"
fi
}
env_get() {
local key=$1
local value=`cat ${cur_path}/.env | grep "^$key=" | awk -F '=' '{print $2}'`
@@ -220,7 +292,7 @@ env_set() {
docker run -it --rm -v ${cur_path}:/www alpine sh -c "sed -i "/^${key}=/c\\${key}=${val}" /www/.env"
fi
if [ $? -ne 0 ]; then
echo -e "${Error} ${RedBG} 设置env参数失败${Font}"
error "设置env参数失败"
exit 1
fi
fi
@@ -293,7 +365,7 @@ if [ $# -gt 0 ]; then
run_exec php "composer config --unset repos.packagist"
fi
if [ ! -f "${cur_path}/vendor/autoload.php" ]; then
echo -e "${Error} ${RedBG}composer install 失败,请重试! ${Font}"
error "composer install 失败,请重试!"
exit 1
fi
[[ -z "$(env_get APP_KEY)" ]] && run_exec php "php artisan key:generate"
@@ -303,7 +375,7 @@ if [ $# -gt 0 ]; then
while [ ! -f "${cur_path}/docker/mysql/data/$(env_get DB_DATABASE)/db.opt" ]; do
((remaining=$remaining-1))
if [ $remaining -lt 0 ]; then
echo -e "${Error} ${RedBG} 数据库初始化失败! ${Font}"
error "数据库初始化失败!"
exit 1
fi
chmod -R 775 "${cur_path}/docker/mysql/data"
@@ -311,19 +383,21 @@ if [ $# -gt 0 ]; then
done
run_exec php "php artisan migrate --seed"
if [ ! -f "${cur_path}/docker/mysql/data/$(env_get DB_DATABASE)/$(env_get DB_PREFIX)migrations.ibd" ]; then
echo -e "${Error} ${RedBG} 数据库安装失败! ${Font}"
error "数据库安装失败!"
exit 1
fi
# 设置初始化密码
res=`run_exec mariadb "sh /etc/mysql/repassword.sh"`
$COMPOSE up -d
restart_php
echo -e "${OK} ${GreenBG} 安装完成 ${Font}"
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
echo -e "$res"
success "安装完成"
info "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
info "$res"
elif [[ "$1" == "update" ]]; then
shift 1
run_mysql backup
if [[ "$@" != "nobackup" ]]; then
run_mysql backup
fi
if [[ -z "$(arg_get local)" ]]; then
git fetch --all
git reset --hard origin/$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')
@@ -340,10 +414,10 @@ if [ $# -gt 0 ]; then
[[ -z ${uninstall} ]] && uninstall="Y"
case $uninstall in
[yY][eE][sS] | [yY])
echo -e "${RedBG} 开始卸载... ${Font}"
info "${RedBG}开始卸载...${Font}"
;;
*)
echo -e "${GreenBG} 终止卸载。 ${Font}"
info "${GreenBG}终止卸载。${Font}"
exit 2
;;
esac
@@ -352,7 +426,7 @@ if [ $# -gt 0 ]; then
rm -rf "./docker/mysql/data"
rm -rf "./docker/log/supervisor"
find "./storage/logs" -name "*.log" | xargs rm -rf
echo -e "${OK} ${GreenBG} 卸载完成 ${Font}"
success "卸载完成"
elif [[ "$1" == "reinstall" ]]; then
shift 1
./cmd uninstall $@
@@ -362,20 +436,20 @@ if [ $# -gt 0 ]; then
shift 1
env_set APP_PORT "$1"
$COMPOSE up -d
echo -e "${OK} ${GreenBG} 修改成功 ${Font}"
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
success "修改成功"
info "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
elif [[ "$1" == "url" ]]; then
shift 1
env_set APP_URL "$1"
restart_php
echo -e "${OK} ${GreenBG} 修改成功 ${Font}"
success "修改成功"
elif [[ "$1" == "env" ]]; then
shift 1
if [ -n "$1" ]; then
env_set $1 "$2"
fi
restart_php
echo -e "${OK} ${GreenBG} 修改成功 ${Font}"
success "修改成功"
elif [[ "$1" == "repassword" ]]; then
shift 1
run_exec mariadb "sh /etc/mysql/repassword.sh \"$@\""
@@ -391,6 +465,16 @@ if [ $# -gt 0 ]; then
elif [[ "$1" == "electron" ]]; then
shift 1
run_electron $@
elif [[ "$1" == "eeui" ]]; then
shift 1
cli="$@"
por=""
if [[ "$cli" == "build" ]]; then
cli="build --simple"
elif [[ "$cli" == "dev" ]]; then
por="-p 8880:8880"
fi
docker run -it --rm -v ${cur_path}/resources/mobile:/work -w /work ${por} kuaifan/eeui-cli:0.0.1 eeui ${cli}
elif [[ "$1" == "doc" ]]; then
shift 1
run_exec php "php app/Http/Controllers/Api/apidoc.php"
@@ -398,13 +482,15 @@ if [ $# -gt 0 ]; then
elif [[ "$1" == "debug" ]]; then
shift 1
switch_debug "$@"
echo "success"
info "success"
elif [[ "$1" == "https" ]]; then
shift 1
if [[ "$@" == "auto" ]]; then
if [[ "$1" == "agent" ]] || [[ "$1" == "true" ]]; then
env_set APP_SCHEME "true"
elif [[ "$1" == "close" ]] || [[ "$1" == "auto" ]]; then
env_set APP_SCHEME "auto"
else
env_set APP_SCHEME "true"
https_auto
fi
restart_php
elif [[ "$1" == "artisan" ]]; then

950
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
<?php
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
use App\Models\User;
use App\Models\WebSocketDialog;
use App\Module\Base;
use Carbon\Carbon;
use Illuminate\Database\Migrations\Migration;
class AllGroupMuteHandle extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$systemConfig = Base::setting('system');
if ($systemConfig['all_group_mute'] == 'user' || $systemConfig['all_group_mute'] == 'all') {
$systemConfig['all_group_mute'] = 'close';
Base::setting('system', $systemConfig);
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
class ChangeCharsetToUtf8mb4 extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$pre = DB::connection()->getTablePrefix();
$tables = [
"approve_execution",
"approve_execution_history",
"approve_identitylink",
"approve_identitylink_history",
"approve_proc_inst",
"approve_proc_inst_history",
"approve_proc_msgs",
"approve_procdef",
"approve_procdef_history",
"approve_task",
"approve_task_history"
];
foreach ($tables as $table) {
if (Schema::hasTable($table)) {
Schema::getConnection()->getPdo()->exec("ALTER TABLE `{$pre}{$table}` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
}
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddWebSocketDialogUsersAddHide extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
if (!Schema::hasColumn('web_socket_dialog_users', 'hide')) {
$table->integer('hide')->nullable()->default(0)->after('silence')->comment('不显示会话0否1是');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
$table->dropColumn("hide");
});
}
}

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIndexSome20240315 extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('web_socket_dialog_msg_reads', function (Blueprint $table) {
$table->index(['dialog_id', 'userid', 'read_at', 'msg_id'], 'IDEX_dialog_id_userid_read_at_msg_id');
});
}
/**
* Reverse the migrations.
*
* @return voidw
*/
public function down()
{
Schema::table('web_socket_dialog_msg_reads', function (Blueprint $table) {
$table->dropIndex('IDEX_dialog_id_userid_read_at_msg_id');
});
}
}

View File

@@ -0,0 +1,50 @@
<?php
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogUser;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddWebSocketDialogUsersLastAt extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$isAdd = false;
Schema::table('web_socket_dialog_users', function (Blueprint $table) use (&$isAdd) {
if (!Schema::hasColumn('web_socket_dialog_users', 'last_at')) {
$isAdd = true;
$table->timestamp('last_at')->nullable()->after('top_at')->comment('最后消息时间');
}
});
if ($isAdd) {
// 更新数据
WebSocketDialog::chunk(100, function ($dialogs) {
/** @var WebSocketDialog $dialog */
foreach ($dialogs as $dialog) {
WebSocketDialogUser::whereDialogId($dialog->id)->update(['last_at' => $dialog->last_at]);
}
});
// 删除表字段
Schema::table('web_socket_dialogs', function (Blueprint $table) {
$table->dropColumn("last_at");
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// 回滚数据 - 无法回滚
}
}

View File

@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIndexSome20240317 extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
$table->dropIndex(['userid']);
$table->index(['userid', 'dialog_id']);
});
}
/**
* Reverse the migrations.
*
* @return voidw
*/
public function down()
{
// 回滚数据 - 无法回滚
}
}

View File

@@ -0,0 +1,55 @@
<?php
use App\Module\Base;
use App\Models\WebSocketDialogMsg;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ChangeForwardData extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
if (Schema::hasColumn('web_socket_dialog_msgs', 'forward_show')) {
WebSocketDialogMsg::where("forward_id", ">", 0)->chunk(100, function ($items) {
/** @var WebSocketDialogMsg $item */
foreach ($items as $item) {
$msg = Base::json2array($item->getRawOriginal('msg'));
$msg['forward_data'] = [
'id' => $item->forward_id,
'userid' => 0,
'parent_id' => $item->forward_id,
'parent_userid' => 0,
'show' => 0,
];
$original = WebSocketDialogMsg::select(['id', 'userid', 'forward_show'])->whereId($item->forward_id)->withTrashed()->first();
if ($original) {
$msg['forward_data']['userid'] = $original->userid;
$msg['forward_data']['parent_userid'] = $original->userid;
$msg['forward_data']['show'] = $original->forward_show;
}
$item->msg = Base::array2json($msg);
$item->save();
}
});
$table->dropColumn("forward_show");
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// 回滚数据 - 无法回滚
}
}

View File

@@ -0,0 +1,49 @@
<?php
use App\Module\Base;
use App\Models\WebSocketDialogMsg;
use Illuminate\Database\Migrations\Migration;
class ChangeReplyData extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
WebSocketDialogMsg::where("reply_id", ">", 0)->chunk(100, function ($items) {
/** @var WebSocketDialogMsg $item */
foreach ($items as $item) {
$msg = Base::json2array($item->getRawOriginal('msg'));
$msg['reply_data'] = [
'id' => $item->reply_id,
'userid' => 0,
'type' => '',
'msg' => [],
];
$original = WebSocketDialogMsg::whereId($item->reply_id)->withTrashed()->first();
if ($original) {
$replyMsg = Base::json2array($original->getRawOriginal('msg'));
unset($replyMsg['reply_data']);
$msg['reply_data']['userid'] = $original->userid;
$msg['reply_data']['type'] = $original->type;
$msg['reply_data']['msg'] = $replyMsg;
}
$item->msg = Base::array2json($msg);
$item->save();
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// 回滚数据 - 无法回滚
}
}

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddUmengAliasDevice extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//
Schema::table('umeng_alias', function (Blueprint $table) {
if (!Schema::hasColumn('umeng_alias', 'device')) {
$table->text('ua')->nullable()->after('platform')->comment('userAgent');
$table->string('device', 100)->nullable()->default('')->after('platform')->comment('设备类型');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
Schema::table('umeng_alias', function (Blueprint $table) {
$table->dropColumn("ua");
$table->dropColumn("device");
});
}
}

View File

@@ -2,6 +2,7 @@
use App\Models\User;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogUser;
use Carbon\Carbon;
use Illuminate\Database\Migrations\Migration;
@@ -19,8 +20,7 @@ class GenerateWebSocketDialogsDefaultGroup extends Migration
if ($botUser) {
$dialog = WebSocketDialog::checkUserDialog($botUser, 1);
if ($dialog) {
$dialog->last_at = Carbon::now();
$dialog->save();
WebSocketDialogUser::whereDialogId($dialog->id)->update(['last_at' => Carbon::now()]);
}
}

View File

@@ -0,0 +1,45 @@
<?php
use App\Models\WebSocketDialogMsg;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMeetingMsgsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::dropIfExists('meeting_msgs');
Schema::create('meeting_msgs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('meetingid')->nullable()->default('')->comment('会议ID');
$table->bigInteger('dialog_id')->nullable()->default(0)->comment('对话ID');
$table->bigInteger('msg_id')->nullable()->default(0)->comment('消息ID');
});
\DB::table('meetings')->update(['end_at' => null]);
WebSocketDialogMsg::whereType('meeting')->chunk(100, function ($msgs) {
/** @var WebSocketDialogMsg $msg */
foreach ($msgs as $msg) {
$meetingid = $msg->msg['meetingid'];
$dialog_id = $msg->dialog_id;
$msg_id = $msg->id;
\DB::table('meeting_msgs')->insert(compact('meetingid', 'dialog_id', 'msg_id'));
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('meeting_msgs');
}
}

View File

@@ -26,6 +26,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 1,
'dialog_id' => 1,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 10:46:37'),
'created_at' => seeders_at('2021-07-01 10:46:37'),
'updated_at' => seeders_at('2021-07-01 10:46:37'),
),
@@ -34,6 +35,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 2,
'dialog_id' => 1,
'userid' => 2,
'last_at' => seeders_at('2021-07-01 10:46:37'),
'created_at' => seeders_at('2021-07-01 10:46:37'),
'updated_at' => seeders_at('2021-07-01 10:46:37'),
),
@@ -42,6 +44,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 3,
'dialog_id' => 2,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 10:46:47'),
'created_at' => seeders_at('2021-07-01 10:46:47'),
'updated_at' => seeders_at('2021-07-01 10:46:47'),
),
@@ -50,6 +53,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 4,
'dialog_id' => 3,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 10:46:47'),
'created_at' => seeders_at('2021-07-01 10:46:47'),
'updated_at' => seeders_at('2021-07-01 10:46:47'),
),
@@ -58,6 +62,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 5,
'dialog_id' => 4,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 10:47:45'),
'created_at' => seeders_at('2021-07-01 10:47:45'),
'updated_at' => seeders_at('2021-07-01 10:47:45'),
),
@@ -66,6 +71,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 6,
'dialog_id' => 5,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 10:47:45'),
'created_at' => seeders_at('2021-07-01 10:47:45'),
'updated_at' => seeders_at('2021-07-01 10:47:45'),
),
@@ -74,6 +80,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 7,
'dialog_id' => 6,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 11:02:57'),
'created_at' => seeders_at('2021-07-01 11:02:57'),
'updated_at' => seeders_at('2021-07-01 11:02:57'),
),
@@ -82,6 +89,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 8,
'dialog_id' => 7,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 11:02:57'),
'created_at' => seeders_at('2021-07-01 11:02:57'),
'updated_at' => seeders_at('2021-07-01 11:02:57'),
),
@@ -90,6 +98,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 9,
'dialog_id' => 8,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 11:43:01'),
'created_at' => seeders_at('2021-07-01 11:43:01'),
'updated_at' => seeders_at('2021-07-01 11:43:01'),
),
@@ -98,6 +107,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 10,
'dialog_id' => 9,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 11:43:01'),
'created_at' => seeders_at('2021-07-01 11:43:01'),
'updated_at' => seeders_at('2021-07-01 11:43:01'),
),
@@ -106,6 +116,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 11,
'dialog_id' => 10,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 15:33:23'),
'created_at' => seeders_at('2021-07-01 15:33:23'),
'updated_at' => seeders_at('2021-07-01 15:33:23'),
),
@@ -114,6 +125,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 12,
'dialog_id' => 11,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 15:33:23'),
'created_at' => seeders_at('2021-07-01 15:33:23'),
'updated_at' => seeders_at('2021-07-01 15:33:23'),
),
@@ -122,6 +134,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 13,
'dialog_id' => 12,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 15:37:06'),
'created_at' => seeders_at('2021-07-01 15:37:06'),
'updated_at' => seeders_at('2021-07-01 15:37:06'),
),
@@ -130,6 +143,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 14,
'dialog_id' => 13,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 15:37:06'),
'created_at' => seeders_at('2021-07-01 15:37:06'),
'updated_at' => seeders_at('2021-07-01 15:37:06'),
),
@@ -138,6 +152,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 15,
'dialog_id' => 14,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 16:15:28'),
'created_at' => seeders_at('2021-07-01 16:15:28'),
'updated_at' => seeders_at('2021-07-01 16:15:28'),
),
@@ -146,6 +161,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 16,
'dialog_id' => 15,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 16:15:28'),
'created_at' => seeders_at('2021-07-01 16:15:28'),
'updated_at' => seeders_at('2021-07-01 16:15:28'),
),
@@ -154,6 +170,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 17,
'dialog_id' => 7,
'userid' => 2,
'last_at' => seeders_at('2021-07-01 16:22:42'),
'created_at' => seeders_at('2021-07-01 16:22:42'),
'updated_at' => seeders_at('2021-07-01 16:22:42'),
),
@@ -162,6 +179,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 18,
'dialog_id' => 5,
'userid' => 2,
'last_at' => seeders_at('2021-07-01 16:23:15'),
'created_at' => seeders_at('2021-07-01 16:23:15'),
'updated_at' => seeders_at('2021-07-01 16:23:15'),
),
@@ -170,6 +188,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 19,
'dialog_id' => 15,
'userid' => 2,
'last_at' => seeders_at('2021-07-01 16:23:40'),
'created_at' => seeders_at('2021-07-01 16:23:40'),
'updated_at' => seeders_at('2021-07-01 16:23:40'),
),
@@ -178,6 +197,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 20,
'dialog_id' => 11,
'userid' => 2,
'last_at' => seeders_at('2021-07-01 16:29:38'),
'created_at' => seeders_at('2021-07-01 16:29:38'),
'updated_at' => seeders_at('2021-07-01 16:29:38'),
),
@@ -186,6 +206,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 21,
'dialog_id' => 16,
'userid' => 2,
'last_at' => seeders_at('2021-07-01 16:56:05'),
'created_at' => seeders_at('2021-07-01 16:56:05'),
'updated_at' => seeders_at('2021-07-01 16:56:05'),
),
@@ -194,6 +215,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 22,
'dialog_id' => 16,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 16:56:05'),
'created_at' => seeders_at('2021-07-01 16:56:05'),
'updated_at' => seeders_at('2021-07-01 16:56:05'),
),
@@ -202,6 +224,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 23,
'dialog_id' => 17,
'userid' => 2,
'last_at' => seeders_at('2021-07-01 16:56:35'),
'created_at' => seeders_at('2021-07-01 16:56:35'),
'updated_at' => seeders_at('2021-07-01 16:56:35'),
),
@@ -210,6 +233,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 24,
'dialog_id' => 17,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 16:56:35'),
'created_at' => seeders_at('2021-07-01 16:56:35'),
'updated_at' => seeders_at('2021-07-01 16:56:35'),
),
@@ -218,6 +242,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 25,
'dialog_id' => 18,
'userid' => 2,
'last_at' => seeders_at('2021-07-01 16:56:57'),
'created_at' => seeders_at('2021-07-01 16:56:57'),
'updated_at' => seeders_at('2021-07-01 16:56:57'),
),
@@ -226,6 +251,7 @@ class WebSocketDialogUsersTableSeeder extends Seeder
'id' => 26,
'dialog_id' => 18,
'userid' => 1,
'last_at' => seeders_at('2021-07-01 16:56:57'),
'created_at' => seeders_at('2021-07-01 16:56:57'),
'updated_at' => seeders_at('2021-07-01 16:56:57'),
),

View File

@@ -4,6 +4,7 @@ namespace Database\Seeders;
use App\Models\User;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogUser;
use Carbon\Carbon;
use Illuminate\Database\Seeder;
@@ -30,7 +31,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'user',
'group_type' => '',
'name' => '',
'last_at' => seeders_at('2021-07-01 17:12:52'),
'created_at' => seeders_at('2021-07-01 10:46:37'),
'updated_at' => seeders_at('2021-07-01 17:12:52'),
'deleted_at' => NULL,
@@ -41,7 +41,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => NULL,
'created_at' => seeders_at('2021-07-01 10:46:47'),
'updated_at' => seeders_at('2021-07-01 17:31:03'),
'deleted_at' => seeders_at('2021-07-01 17:31:03'),
@@ -52,7 +51,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => NULL,
'created_at' => seeders_at('2021-07-01 10:46:47'),
'updated_at' => seeders_at('2021-07-01 10:46:55'),
'deleted_at' => seeders_at('2021-07-01 10:46:55'),
@@ -63,7 +61,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => NULL,
'created_at' => seeders_at('2021-07-01 10:47:45'),
'updated_at' => seeders_at('2021-07-01 17:30:05'),
'deleted_at' => seeders_at('2021-07-01 17:30:05'),
@@ -74,7 +71,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => seeders_at('2021-07-01 16:31:58'),
'created_at' => seeders_at('2021-07-01 10:47:45'),
'updated_at' => seeders_at('2021-07-01 16:31:58'),
'deleted_at' => NULL,
@@ -85,7 +81,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => NULL,
'created_at' => seeders_at('2021-07-01 11:02:57'),
'updated_at' => seeders_at('2021-07-01 17:31:28'),
'deleted_at' => seeders_at('2021-07-01 17:31:28'),
@@ -96,7 +91,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => seeders_at('2021-07-01 16:32:50'),
'created_at' => seeders_at('2021-07-01 11:02:57'),
'updated_at' => seeders_at('2021-07-01 16:32:50'),
'deleted_at' => NULL,
@@ -107,7 +101,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => NULL,
'created_at' => seeders_at('2021-07-01 11:43:01'),
'updated_at' => seeders_at('2021-07-01 17:31:28'),
'deleted_at' => seeders_at('2021-07-01 17:31:28'),
@@ -118,7 +111,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => seeders_at('2021-07-01 14:02:57'),
'created_at' => seeders_at('2021-07-01 11:43:01'),
'updated_at' => seeders_at('2021-07-01 14:02:57'),
'deleted_at' => NULL,
@@ -129,7 +121,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => NULL,
'created_at' => seeders_at('2021-07-01 15:33:23'),
'updated_at' => seeders_at('2021-07-01 17:31:28'),
'deleted_at' => seeders_at('2021-07-01 17:31:28'),
@@ -140,7 +131,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => seeders_at('2021-07-01 16:31:40'),
'created_at' => seeders_at('2021-07-01 15:33:23'),
'updated_at' => seeders_at('2021-07-01 16:31:40'),
'deleted_at' => NULL,
@@ -151,7 +141,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => NULL,
'created_at' => seeders_at('2021-07-01 15:37:06'),
'updated_at' => seeders_at('2021-07-01 17:31:28'),
'deleted_at' => seeders_at('2021-07-01 17:31:28'),
@@ -162,7 +151,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => seeders_at('2021-07-01 15:44:09'),
'created_at' => seeders_at('2021-07-01 15:37:06'),
'updated_at' => seeders_at('2021-07-01 15:44:09'),
'deleted_at' => NULL,
@@ -173,7 +161,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => NULL,
'created_at' => seeders_at('2021-07-01 16:15:28'),
'updated_at' => seeders_at('2021-07-01 17:31:28'),
'deleted_at' => seeders_at('2021-07-01 17:31:28'),
@@ -184,7 +171,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'project',
'name' => '',
'last_at' => seeders_at('2021-07-01 16:37:59'),
'created_at' => seeders_at('2021-07-01 16:15:28'),
'updated_at' => seeders_at('2021-07-01 16:37:59'),
'deleted_at' => NULL,
@@ -195,7 +181,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'task',
'name' => '',
'last_at' => seeders_at('2021-07-01 16:58:02'),
'created_at' => seeders_at('2021-07-01 16:56:05'),
'updated_at' => seeders_at('2021-07-01 16:58:02'),
'deleted_at' => NULL,
@@ -206,7 +191,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'task',
'name' => '',
'last_at' => seeders_at('2021-07-01 16:57:58'),
'created_at' => seeders_at('2021-07-01 16:56:35'),
'updated_at' => seeders_at('2021-07-01 16:57:58'),
'deleted_at' => NULL,
@@ -217,7 +201,6 @@ class WebSocketDialogsTableSeeder extends Seeder
'type' => 'group',
'group_type' => 'task',
'name' => '',
'last_at' => seeders_at('2021-07-01 16:57:52'),
'created_at' => seeders_at('2021-07-01 16:56:57'),
'updated_at' => seeders_at('2021-07-01 16:57:52'),
'deleted_at' => NULL,
@@ -228,8 +211,7 @@ class WebSocketDialogsTableSeeder extends Seeder
if ($botUser) {
$dialog = WebSocketDialog::checkUserDialog($botUser, 1);
if ($dialog) {
$dialog->last_at = Carbon::now();
$dialog->save();
WebSocketDialogUser::whereDialogId($dialog->id)->change(['last_at' => Carbon::now()->subSecond()]);
}
}
User::botGetOrCreate('ai-openai');

View File

@@ -36,6 +36,7 @@ services:
image: "nginx:alpine"
ports:
- "${APP_PORT}:80"
- "${APP_SSL_PORT:-}:443"
volumes:
- ./docker/nginx:/etc/nginx/conf.d
- ./public:/var/www/public
@@ -103,7 +104,7 @@ services:
fileview:
container_name: "dootask-fileview-${APP_ID}"
image: "kuaifan/fileview:4.2.0-SNAPSHOT-RC22"
image: "kuaifan/fileview:4.2.0-SNAPSHOT-RC25"
environment:
KK_CONTEXT_PATH: "/fileview"
KK_OFFICE_PREVIEW_SWITCH_DISABLED: true
@@ -160,6 +161,7 @@ services:
MYSQL_PASSWORD: "${DB_PASSWORD}"
MYSQL_Prefix: "${DB_PREFIX}approve_"
DEMO_DATA: true
KEY: ${APP_KEY}
networks:
extnetwork:
ipv4_address: "${APP_IPPR}.11"
@@ -169,7 +171,7 @@ services:
ai:
container_name: "dootask-ai-${APP_ID}"
image: "kuaifan/dooai:0.1.2"
image: "kuaifan/dooai:0.1.8"
networks:
extnetwork:
ipv4_address: "${APP_IPPR}.12"
@@ -177,7 +179,7 @@ services:
okr:
container_name: "dootask-okr-${APP_ID}"
image: "kuaifan/doookr:0.0.30"
image: "kuaifan/doookr:0.4.2"
environment:
TZ: "${TIMEZONE:-PRC}"
DOO_TASK_URL: "http://${APP_IPPR}.3"
@@ -188,6 +190,7 @@ services:
MYSQL_PASSWORD: "${DB_PASSWORD}"
MYSQL_PREFIX: "${DB_PREFIX}"
DEMO_DATA: true
KEY: ${APP_KEY}
networks:
extnetwork:
ipv4_address: "${APP_IPPR}.13"

View File

@@ -12,7 +12,7 @@ default_storage_engine = InnoDB
performance_schema_max_table_instances = 400
table_definition_cache = 400
key_buffer_size = 1024M
max_allowed_packet = 10G
max_allowed_packet = 8192M
table_open_cache = 2048
sort_buffer_size = 4096K
net_buffer_length = 4K
@@ -47,7 +47,7 @@ innodb_write_io_threads = 2
[mysqldump]
quick
max_allowed_packet = 500M
max_allowed_packet = 8192M
[mysql]
no-auto-rehash

View File

@@ -1 +0,0 @@
site

View File

@@ -1 +1 @@
/*.conf
!.gitignore

1
docker/nginx/site/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
!.gitignore

View File

@@ -52,7 +52,6 @@
window._disableDownload = setInterval(function() {
var downloadBtn = document.getElementById("slot-hbtn-download")
if(downloadBtn) {
console.log(1)
clearInterval(window._disableDownload);
document.getElementById("slot-hbtn-download")?.remove()
document.getElementById("fm-btn-download")?.remove()

23
electron/build.js vendored
View File

@@ -126,6 +126,16 @@ function genericPublish({url, key, version, output}) {
if (err) {
console.warn(err)
} else {
let uploadFileNum = 0;
for (const filename of files) {
const localFile = path.join(filePath, filename)
if (fs.existsSync(localFile)) {
const fileStat = fs.statSync(localFile)
if (fileStat.isFile()) {
uploadFileNum += 1;
}
}
}
const uploadOras = {}
for (const filename of files) {
const localFile = path.join(filePath, filename)
@@ -135,6 +145,7 @@ function genericPublish({url, key, version, output}) {
uploadOras[filename] = ora(`Upload [0%] ${filename}`).start()
const formData = new FormData()
formData.append("file", fs.createReadStream(localFile));
formData.append("file_num", uploadFileNum);
await axiosAutoTry({
axios: {
method: 'post',
@@ -218,14 +229,16 @@ function startBuild(data) {
fs.writeFileSync(indexFile, indexString, 'utf8');
//
if (data.id === 'app') {
const eeuiDir = path.resolve(__dirname, "../resources/mobile");
const eeuiCli = "kuaifan/eeui-cli:0.0.1"
const publicDir = path.resolve(__dirname, "../resources/mobile/src/public");
fse.removeSync(publicDir)
fse.copySync(electronDir, publicDir)
if (argv[3] === "setting") {
child_process.spawnSync("eeui", ["setting"], {stdio: "inherit", cwd: "resources/mobile"});
child_process.spawnSync("docker", `run -it --rm -v ${eeuiDir}:/work -w /work ${eeuiCli} eeui setting`.split(" "), {stdio: "inherit", cwd: "resources/mobile"});
}
if (['setting', 'build'].includes(argv[3])) {
child_process.spawnSync("eeui", ["build", "--simple"], {stdio: "inherit", cwd: "resources/mobile"});
child_process.spawnSync("docker", `run -it --rm -v ${eeuiDir}:/work -w /work ${eeuiCli} eeui build --simple`.split(" "), {stdio: "inherit", cwd: "resources/mobile"});
} else {
[
path.resolve(publicDir, "../../platforms/ios/eeuiApp/bundlejs/eeui/public"),
@@ -325,9 +338,11 @@ if (["dev"].includes(argv[2])) {
notarize: false,
}
}, false, false)
} else if (["all"].includes(argv[2])) {
} else if (["all", "win", "mac"].includes(argv[2])) {
// 自动编译
platforms.forEach(platform => {
platforms.filter(p => {
return argv[2] === "all" || p.indexOf(argv[2]) !== -1
}).forEach(platform => {
config.app.forEach(data => {
data.configure = {
platform,

View File

@@ -72,6 +72,7 @@ contextBridge.exposeInMainWorld(
contextBridge.exposeInMainWorld(
'process', {
type: process.type,
versions: process.versions
versions: process.versions,
platform: process.platform,
}
);

522
electron/electron.js vendored
View File

@@ -1,9 +1,11 @@
const fs = require('fs')
const os = require("os");
const path = require('path')
const {app, BrowserWindow, ipcMain, dialog, clipboard, nativeImage, shell, Tray, Menu, globalShortcut, Notification} = require('electron')
const {app, BrowserWindow, ipcMain, dialog, clipboard, nativeImage, shell, Tray, Menu, globalShortcut, Notification, BrowserView, nativeTheme} = require('electron')
const {autoUpdater} = require("electron-updater")
const log = require("electron-log");
const electronConf = require('electron-config')
const userConf = new electronConf()
const fsProm = require('fs/promises');
const PDFDocument = require('pdf-lib').PDFDocument;
const Screenshots = require("electron-screenshots-tool").Screenshots;
@@ -17,13 +19,13 @@ const spawn = require("child_process").spawn;
const isMac = process.platform === 'darwin'
const isWin = process.platform === 'win32'
const allowedUrls = /^(?:https?|mailto|tel|callto):/i;
const allowedCalls = /^(?:mailto|tel|callto):/i;
let enableStoreBkp = true;
let dialogOpen = false;
let enablePlugins = false;
let mainWindow = null,
mainTray = null,
subWindow = [],
isReady = false,
willQuitApp = false,
devloadUrl = "",
@@ -32,6 +34,20 @@ let mainWindow = null,
let screenshotObj = null,
screenshotKey = null;
let childWindow = [],
webTabWindow = null,
webTabView = [],
webTabHeight = 38;
let showState = {},
onShowWindow = (win) => {
if (typeof showState[win.webContents.id] === 'undefined') {
showState[win.webContents.id] = true
win.setBackgroundColor('rgba(255, 255, 255, 0)')
win.show();
}
}
if (fs.existsSync(devloadCachePath)) {
devloadUrl = fs.readFileSync(devloadCachePath, 'utf8')
}
@@ -43,6 +59,8 @@ function createMainWindow() {
mainWindow = new BrowserWindow({
width: 1280,
height: 800,
minWidth: 360,
minHeight: 360,
center: true,
autoHideMenuBar: true,
webPreferences: {
@@ -56,19 +74,20 @@ function createMainWindow() {
const originalUA = mainWindow.webContents.session.getUserAgent() || mainWindow.webContents.getUserAgent()
mainWindow.webContents.setUserAgent(originalUA + " MainTaskWindow/" + process.platform + "/" + os.arch() + "/1.0");
mainWindow.webContents.setWindowOpenHandler(({url}) => {
openExternal(url)
if (allowedCalls.test(url)) {
return {action: 'allow'}
}
utils.onBeforeOpenWindow(mainWindow.webContents, url).then(() => {
openExternal(url)
})
return {action: 'deny'}
})
electronMenu.webContentsMenu(mainWindow.webContents)
if (devloadUrl) {
mainWindow.loadURL(devloadUrl).then(_ => {
})
mainWindow.loadURL(devloadUrl).then(_ => { }).catch(_ => { })
} else {
mainWindow.loadFile('./public/index.html').then(_ => {
})
mainWindow.loadFile('./public/index.html').then(_ => { }).catch(_ => { })
}
mainWindow.on('page-title-updated', (event, title) => {
@@ -96,7 +115,7 @@ function createMainWindow() {
* 创建子窗口
* @param args {path, hash, title, titleFixed, force, userAgent, config, webPreferences}
*/
function createSubWindow(args) {
function createChildWindow(args) {
if (!args) {
return;
}
@@ -106,7 +125,7 @@ function createSubWindow(args) {
}
let name = args.name || "auto_" + utils.randomString(6);
let item = subWindow.find(item => item.name == name);
let item = childWindow.find(item => item.name == name);
let browser = item ? item.browser : null;
if (browser) {
browser.focus();
@@ -119,7 +138,10 @@ function createSubWindow(args) {
browser = new BrowserWindow(Object.assign({
width: 1280,
height: 800,
minWidth: 360,
minHeight: 360,
center: true,
show: false,
parent: mainWindow,
autoHideMenuBar: true,
webPreferences: Object.assign({
@@ -146,34 +168,49 @@ function createSubWindow(args) {
})
browser.on('closed', () => {
let index = subWindow.findIndex(item => item.name == name);
let index = childWindow.findIndex(item => item.name == name);
if (index > -1) {
subWindow.splice(index, 1)
childWindow.splice(index, 1)
}
})
subWindow.push({ name, browser })
browser.once('ready-to-show', () => {
onShowWindow(browser);
})
browser.webContents.once('dom-ready', () => {
onShowWindow(browser);
})
childWindow.push({ name, browser })
}
const originalUA = browser.webContents.session.getUserAgent() || browser.webContents.getUserAgent()
browser.webContents.setUserAgent(originalUA + " SubTaskWindow/" + process.platform + "/" + os.arch() + "/1.0" + (args.userAgent ? (" " + args.userAgent) : ""));
browser.webContents.setWindowOpenHandler(({url}) => {
openExternal(url)
if (allowedCalls.test(url)) {
return {action: 'allow'}
}
utils.onBeforeOpenWindow(browser.webContents, url).then(() => {
openExternal(url)
})
return {action: 'deny'}
})
electronMenu.webContentsMenu(browser.webContents)
const hash = args.hash || args.path;
if (devloadUrl) {
browser.loadURL(devloadUrl + '#' + hash).then(_ => {
})
} else {
browser.loadFile('./public/index.html', {
hash
}).then(_ => {
})
if (/^https?:\/\//i.test(hash)) {
browser.loadURL(hash).then(_ => { }).catch(_ => { })
return;
}
if (devloadUrl) {
browser.loadURL(devloadUrl + '#' + hash).then(_ => { }).catch(_ => { })
return;
}
browser.loadFile('./public/index.html', {
hash
}).then(_ => {
})
}
/**
@@ -181,7 +218,7 @@ function createSubWindow(args) {
* @param browser
* @param args
*/
function updateSubWindow(browser, args) {
function updateChildWindow(browser, args) {
if (!args) {
return;
}
@@ -193,25 +230,345 @@ function updateSubWindow(browser, args) {
const hash = args.hash || args.path;
if (hash) {
if (devloadUrl) {
browser.loadURL(devloadUrl + '#' + hash).then(_ => {
})
browser.loadURL(devloadUrl + '#' + hash).then(_ => { }).catch(_ => { })
} else {
browser.loadFile('./public/index.html', {
hash
}).then(_ => {
})
}).then(_ => { }).catch(_ => { })
}
}
if (args.name) {
const er = subWindow.find(item => item.browser == browser);
const er = childWindow.find(item => item.browser == browser);
if (er) {
er.name = args.name;
}
}
}
/**
* 创建内置浏览器
* @param args {url, ?}
*/
function createWebTabWindow(args) {
if (!args) {
return;
}
if (!utils.isJson(args)) {
args = {url: args}
}
if (!allowedUrls.test(args.url)) {
return;
}
// 创建父级窗口
if (!webTabWindow) {
let config = Object.assign(args.config || {}, userConf.get('webTabWindow', {}));
let webPreferences = args.webPreferences || {};
const titleBarOverlay = {
height: webTabHeight
}
if (nativeTheme.shouldUseDarkColors) {
titleBarOverlay.color = '#3B3B3D'
titleBarOverlay.symbolColor = '#C5C5C5'
}
webTabWindow = new BrowserWindow(Object.assign({
x: mainWindow.getBounds().x + webTabHeight,
y: mainWindow.getBounds().y + webTabHeight,
width: 1280,
height: 800,
minWidth: 360,
minHeight: 360,
center: true,
show: false,
autoHideMenuBar: true,
titleBarStyle: 'hidden',
titleBarOverlay,
webPreferences: Object.assign({
preload: path.join(__dirname, 'electron-preload.js'),
webSecurity: true,
nodeIntegration: true,
contextIsolation: true,
nativeWindowOpen: true
}, webPreferences),
}, config))
if (nativeTheme.shouldUseDarkColors) {
webTabWindow.setBackgroundColor('#3B3B3D')
} else {
webTabWindow.setBackgroundColor('#EFF0F4')
}
webTabWindow.on('resize', () => {
resizeWebTab(0)
})
webTabWindow.on('enter-full-screen', () => {
utils.onDispatchEvent(webTabWindow.webContents, {
event: 'enter-full-screen',
}).then(_ => { })
})
webTabWindow.on('leave-full-screen', () => {
utils.onDispatchEvent(webTabWindow.webContents, {
event: 'leave-full-screen',
}).then(_ => { })
})
webTabWindow.on('close', event => {
if (!willQuitApp) {
closeWebTab(0)
event.preventDefault()
} else {
userConf.set('webTabWindow', webTabWindow.getBounds())
}
})
webTabWindow.on('closed', () => {
webTabView.forEach(({view}) => {
try {
view.webContents.close()
} catch (e) {
//
}
})
webTabView = []
webTabWindow = null
})
webTabWindow.once('ready-to-show', () => {
onShowWindow(webTabWindow);
})
webTabWindow.webContents.once('dom-ready', () => {
onShowWindow(webTabWindow);
})
webTabWindow.webContents.on('before-input-event', (event, input) => {
if (utils.isMetaOrControl(input) && input.key.toLowerCase() === 'r') {
reloadWebTab(0)
event.preventDefault()
} else if (utils.isMetaOrControl(input) && input.shift && input.key.toLowerCase() === 'i') {
devToolsWebTab(0)
}
})
webTabWindow.loadFile('./render/tabs/index.html', {}).then(_ => {
})
}
if (webTabWindow.isMinimized()) {
webTabWindow.restore()
}
webTabWindow.focus();
webTabWindow.show();
// 创建子窗口
const browserView = new BrowserView({
useHTMLTitleAndIcon: true,
useLoadingView: true,
useErrorView: true,
webPreferences: {
preload: path.join(__dirname, 'electron-preload.js'),
}
})
if (nativeTheme.shouldUseDarkColors) {
browserView.setBackgroundColor('#575757')
} else {
browserView.setBackgroundColor('#FFFFFF')
}
browserView.setBounds({
x: 0,
y: webTabHeight,
width: webTabWindow.getContentBounds().width || 1280,
height: (webTabWindow.getContentBounds().height || 800) - webTabHeight,
})
browserView.webContents.on('destroyed', () => {
closeWebTab(browserView.webContents.id)
})
browserView.webContents.setWindowOpenHandler(({url}) => {
if (allowedCalls.test(url)) {
return {action: 'allow'}
}
createWebTabWindow({url})
return {action: 'deny'}
})
browserView.webContents.on('page-title-updated', (event, title) => {
utils.onDispatchEvent(webTabWindow.webContents, {
event: 'title',
id: browserView.webContents.id,
title: title,
url: browserView.webContents.getURL(),
}).then(_ => { })
})
browserView.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL, isMainFrame) => {
if (!errorDescription) {
return
}
utils.onDispatchEvent(webTabWindow.webContents, {
event: 'title',
id: browserView.webContents.id,
title: errorDescription,
url: browserView.webContents.getURL(),
}).then(_ => { })
})
browserView.webContents.on('page-favicon-updated', (event, favicons) => {
utils.onDispatchEvent(webTabWindow.webContents, {
event: 'favicon',
id: browserView.webContents.id,
favicons
}).then(_ => { })
})
browserView.webContents.on('did-start-loading', _ => {
utils.onDispatchEvent(webTabWindow.webContents, {
event: 'start-loading',
id: browserView.webContents.id,
}).then(_ => { })
})
browserView.webContents.on('did-stop-loading', _ => {
utils.onDispatchEvent(webTabWindow.webContents, {
event: 'stop-loading',
id: browserView.webContents.id,
}).then(_ => { })
// 加载完成暗黑模式下把窗口背景色改成白色,避免透明网站背景色穿透
if (nativeTheme.shouldUseDarkColors) {
browserView.setBackgroundColor('#FFFFFF')
}
})
browserView.webContents.on('before-input-event', (event, input) => {
if (utils.isMetaOrControl(input) && input.key.toLowerCase() === 'r') {
browserView.webContents.reload()
event.preventDefault()
} else if (utils.isMetaOrControl(input) && input.shift && input.key.toLowerCase() === 'i') {
browserView.webContents.toggleDevTools()
}
})
browserView.webContents.loadURL(args.url).then(_ => { }).catch(_ => { })
webTabWindow.addBrowserView(browserView)
webTabView.push({
id: browserView.webContents.id,
view: browserView
})
utils.onDispatchEvent(webTabWindow.webContents, {
event: 'create',
id: browserView.webContents.id,
url: args.url,
}).then(_ => { })
activateWebTab(browserView.webContents.id)
}
/**
* 获取当前内置浏览器标签
* @returns {Electron.BrowserView|undefined}
*/
function currentWebTab() {
const views = webTabWindow.getBrowserViews()
const view = views.length ? views[views.length - 1] : undefined
if (!view) {
return undefined
}
return webTabView.find(item => item.id == view.webContents.id)
}
/**
* 重新加载内置浏览器标签
* @param id
*/
function reloadWebTab(id) {
const item = id === 0 ? currentWebTab() : webTabView.find(item => item.id == id)
if (!item) {
return
}
item.view.webContents.reload()
}
/**
* 内置浏览器标签打开开发者工具
* @param id
*/
function devToolsWebTab(id) {
const item = id === 0 ? currentWebTab() : webTabView.find(item => item.id == id)
if (!item) {
return
}
item.view.webContents.toggleDevTools()
}
/**
* 调整内置浏览器标签尺寸
* @param id
*/
function resizeWebTab(id) {
const item = id === 0 ? currentWebTab() : webTabView.find(item => item.id == id)
if (!item) {
return
}
item.view.setBounds({
x: 0,
y: webTabHeight,
width: webTabWindow.getContentBounds().width || 1280,
height: (webTabWindow.getContentBounds().height || 800) - webTabHeight,
})
}
/**
* 切换内置浏览器标签
* @param id
*/
function activateWebTab(id) {
const item = id === 0 ? currentWebTab() : webTabView.find(item => item.id == id)
if (!item) {
return
}
resizeWebTab(item.id)
webTabWindow.setTopBrowserView(item.view)
item.view.webContents.focus()
utils.onDispatchEvent(webTabWindow.webContents, {
event: 'switch',
id: item.id,
}).then(_ => { })
}
/**
* 关闭内置浏览器标签
* @param id
*/
function closeWebTab(id) {
const item = id === 0 ? currentWebTab() : webTabView.find(item => item.id == id)
if (!item) {
return
}
if (webTabView.length === 1) {
webTabWindow.hide()
}
webTabWindow.removeBrowserView(item.view)
try {
item.view.webContents.close()
} catch (e) {
//
}
const index = webTabView.findIndex(({id}) => item.id == id)
if (index > -1) {
webTabView.splice(index, 1)
}
utils.onDispatchEvent(webTabWindow.webContents, {
event: 'close',
id: item.id,
}).then(_ => { })
if (webTabView.length === 0) {
userConf.set('webTabWindow', webTabWindow.getBounds())
webTabWindow.destroy()
} else {
activateWebTab(0)
}
}
const getTheLock = app.requestSingleInstanceLock()
if (!getTheLock) {
app.quit()
@@ -329,8 +686,8 @@ ipcMain.on('windowQuit', (event) => {
* 创建路由窗口
* @param args {path, ?}
*/
ipcMain.on('windowRouter', (event, args) => {
createSubWindow(args)
ipcMain.on('openChildWindow', (event, args) => {
createChildWindow(args)
event.returnValue = "ok"
})
@@ -338,9 +695,89 @@ ipcMain.on('windowRouter', (event, args) => {
* 更新路由窗口
* @param args {?name, ?path} // name: 不是要更改的窗口名,是要把窗口名改成什么, path: 地址
*/
ipcMain.on('updateChildWindow', (event, args) => {
const browser = BrowserWindow.fromWebContents(event.sender);
updateChildWindow(browser, args)
event.returnValue = "ok"
})
/**
* 创建路由窗口todo 已废弃)
* @param args {path, ?}
*/
ipcMain.on('windowRouter', (event, args) => {
createChildWindow(args)
event.returnValue = "ok"
})
/**
* 更新路由窗口todo 已废弃)
* @param args {?name, ?path} // name: 不是要更改的窗口名,是要把窗口名改成什么, path: 地址
*/
ipcMain.on('updateRouter', (event, args) => {
const browser = BrowserWindow.fromWebContents(event.sender);
updateSubWindow(browser, args)
updateChildWindow(browser, args)
event.returnValue = "ok"
})
/**
* 内置浏览器 - 打开创建
* @param args {url, ?}
*/
ipcMain.on('openWebTabWindow', (event, args) => {
createWebTabWindow(args)
event.returnValue = "ok"
})
/**
* 内置浏览器 - 激活标签
* @param id
*/
ipcMain.on('webTabActivate', (event, id) => {
activateWebTab(id)
event.returnValue = "ok"
})
/**
* 内置浏览器 - 关闭标签
* @param id
*/
ipcMain.on('webTabClose', (event, id) => {
closeWebTab(id)
event.returnValue = "ok"
})
/**
* 内置浏览器 - 在外部浏览器打开
*/
ipcMain.on('webTabExternal', (event) => {
const item = currentWebTab()
if (!item) {
return
}
openExternal(item.view.webContents.getURL())
event.returnValue = "ok"
})
/**
* 内置浏览器 - 打开开发者工具
*/
ipcMain.on('webTabOpenDevTools', (event) => {
const item = currentWebTab()
if (!item) {
return
}
item.view.webContents.openDevTools()
event.returnValue = "ok"
})
/**
* 内置浏览器 - 销毁所有标签及窗口
*/
ipcMain.on('webTabDestroyAll', (event) => {
if (webTabWindow) {
webTabWindow.destroy()
}
event.returnValue = "ok"
})
@@ -377,8 +814,8 @@ ipcMain.on('windowDestroy', (event) => {
/**
* 关闭所有子窗口
*/
ipcMain.on('subWindowCloseAll', (event) => {
subWindow.some(({browser}) => {
ipcMain.on('childWindowCloseAll', (event) => {
childWindow.some(({browser}) => {
browser && browser.close()
})
event.returnValue = "ok"
@@ -387,8 +824,8 @@ ipcMain.on('subWindowCloseAll', (event) => {
/**
* 销毁所有子窗口
*/
ipcMain.on('subWindowDestroyAll', (event) => {
subWindow.some(({browser}) => {
ipcMain.on('childWindowDestroyAll', (event) => {
childWindow.some(({browser}) => {
browser && browser.destroy()
})
event.returnValue = "ok"
@@ -644,6 +1081,9 @@ ipcMain.on('updateCheckAndDownload', (event, args) => {
autoUpdater.setFeedURL(args)
}
autoUpdater.checkForUpdates().then(info => {
if (!info) {
return
}
if (utils.compareVersion(config.version, info.updateInfo.version) >= 0) {
return
}
@@ -685,7 +1125,7 @@ ipcMain.on('mainWindowActive', (event) => {
ipcMain.on('updateQuitAndInstall', (event) => {
event.returnValue = "ok"
willQuitApp = true
subWindow.some(({browser}) => {
childWindow.some(({browser}) => {
browser && browser.destroy()
})
setTimeout(_ => {

View File

@@ -26,14 +26,14 @@
"url": "https://github.com/kuaifan/dootask.git"
},
"devDependencies": {
"@electron-forge/cli": "^7.2.0",
"@electron-forge/maker-deb": "^7.2.0",
"@electron-forge/maker-rpm": "^7.2.0",
"@electron-forge/maker-squirrel": "^7.2.0",
"@electron-forge/maker-zip": "^7.2.0",
"@electron-forge/cli": "^7.3.1",
"@electron-forge/maker-deb": "^7.3.1",
"@electron-forge/maker-rpm": "^7.3.1",
"@electron-forge/maker-squirrel": "^7.3.1",
"@electron-forge/maker-zip": "^7.3.1",
"dotenv": "^16.3.1",
"electron": "^28.1.1",
"electron-builder": "^24.9.1",
"electron": "^29.2.0",
"electron-builder": "^24.13.3",
"electron-notarize": "^1.2.2",
"form-data": "^4.0.0",
"ora": "^4.1.1"
@@ -41,10 +41,11 @@
"dependencies": {
"axios": "^1.6.2",
"crc": "^3.8.0",
"electron-log": "^5.0.1",
"electron-config": "^2.0.0",
"electron-log": "^5.1.1",
"electron-screenshots-tool": "^1.1.2",
"electron-squirrel-startup": "^1.0.0",
"electron-updater": "^6.1.7",
"electron-updater": "^6.1.8",
"fs-extra": "^11.2.0",
"pdf-lib": "^1.17.1",
"request": "^2.88.2"
@@ -67,6 +68,7 @@
"output": "dist"
},
"files": [
"render/**/*",
"public/**/*",
"electron-menu.js",
"electron-preload.js",

View File

@@ -0,0 +1,269 @@
:root {
--tab-font-family: -apple-system, 'Segoe UI', roboto, oxygen-sans, ubuntu, cantarell, 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
--tab-font-size: 12px;
--tab-transition: background-color 200ms ease-out, color 200ms ease-out;
--tab-cursor: pointer; /* 设置鼠标指针为手型 */
--tab-color: #7f8792;
--tab-background: #EFF0F4;
--tab-active-color: #222529;
--tab-active-background: #FFFFFF;
--tab-close-color: #9DA3AC;
}
* {
margin: 0;
padding: 0;
}
html, body {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
font-size: 16px;
color: #333;
}
.nav {
font-family: var(--tab-font-family);
font-feature-settings: 'clig', 'kern';
display: flex;
width: 100%;
cursor: default;
background-color: var(--tab-background);
-webkit-app-region: drag;
}
.nav ul {
display: flex;
height: 30px;
margin: 8px 46px 0 0;
user-select: none;
overflow-x: auto;
overflow-y: hidden;
}
.nav ul::-webkit-scrollbar {
display: none;
}
.nav ul li {
display: inline-flex;
position: relative;
box-sizing: border-box;
align-items: center;
height: 100%;
padding: 7px 8px;
margin: 0 8px 0 0;
min-width: 100px;
max-width: 240px;
scroll-margin: 12px;
font-size: var(--tab-font-size);
color: var(--tab-color);
cursor: var(--tab-cursor);
transition: var(--tab-transition);
-webkit-app-region: none;
}
.nav ul li:first-child {
margin-left: 8px;
border-left: none;
}
.nav ul li.active {
color: var(--tab-active-color);
background: var(--tab-active-background);
border-radius: 6px 6px 0 0;
}
.nav ul li.active::before {
position: absolute;
bottom: 0;
left: -6px;
width: 6px;
height: 6px;
background-image: url(../image/select_left.png);
background-repeat: no-repeat;
background-size: cover;
content: '';
}
.nav ul li.active::after {
position: absolute;
right: -6px;
bottom: 0;
width: 6px;
height: 6px;
background-image: url(../image/select_right.png);
background-repeat: no-repeat;
background-size: cover;
content: '';
}
.nav ul li.active .tab-icon.background {
background-image: url(../image/link_normal_selected_icon.png);
}
.nav ul li:not(.active)::after {
position: absolute;
right: 0;
width: 1px;
height: 16px;
background: rgba(0, 0, 0, 0.08);
content: '';
}
.nav ul li:not(.active):last-child::after {
content: none;
}
/* 浏览器打开 */
.browser {
position: absolute;
top: 0;
right: 0;
display: flex;
align-items: center;
height: 38px;
padding: 0 14px;
cursor: pointer;
-webkit-app-region: none;
}
.browser span {
display: inline-block;
width: 18px;
height: 18px;
background-size: cover;
background-image: url(../image/link_normal_selected_icon.png);
}
/* 图标 */
.tab-icon {
display: inline-block;
flex-shrink: 0;
width: 18px;
height: 18px;
background-size: cover;
}
.tab-icon.background {
background-image: url(../image/link_normal_icon.png);
}
.tab-icon.loading {
background-image: none !important;
}
.tab-icon .tab-icon-loading {
width: 18px;
height: 18px;
border: 2px solid #eeeeee;
border-bottom-color: #84C56A;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: spin 0.75s linear infinite;
}
.tab-icon:not(.loading) .tab-icon-loading {
display: none;
}
.tab-icon img {
width: 16px;
height: 16px;
border-radius: 4px;
}
@keyframes spin {
0% {
transform: scale(0.8) rotate(0deg);
}
100% {
transform: scale(0.8) rotate(360deg);
}
}
/* 标题 */
.tab-title {
display: inline-block;
flex: 1;
margin-right: 8px;
margin-left: 6px;
overflow: hidden;
line-height: 150%;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 关闭 */
.tab-close {
display: inline-block;
width: 14px;
height: 14px;
margin-right: 2px;
position: relative;
}
.tab-close::after,
.tab-close::before {
position: absolute;
top: 50%;
right: 50%;
transform: translate(50%, -50%) scale(0.9) rotate(45deg);
content: "";
width: 2px;
height: 11px;
border-radius: 3px;
background-color: var(--tab-close-color);
}
.tab-close::before {
transform: translate(50%, -50%) scale(0.9) rotate(-45deg);
}
/* 不同平台样式 */
body.win32 .nav ul {
margin-left: 8px;
margin-right: 186px;
}
body.win32 .browser {
right: 140px;
}
body.darwin .nav ul {
margin-left: 76px;
}
body.darwin.full-screen .nav ul {
margin-left: 8px;
}
/* 暗黑模式 */
@media (prefers-color-scheme: dark) {
:root {
--tab-color: #C5C5C5;
--tab-background: #3B3B3D;
--tab-active-color: #E1E1E1;
--tab-active-background: #575757;
--tab-close-color: #E3E3E3;
}
.nav ul li.active::before {
background-image: url(../image/dark/select_left.png);
}
.nav ul li.active::after {
background-image: url(../image/dark/select_right.png);
}
.nav ul li.active .tab-icon.background {
background-image: url(../image/dark/link_normal_selected_icon.png);
}
.browser span {
background-image: url(../image/dark/link_normal_selected_icon.png);
}
.tab-icon.background {
background-image: url(../image/dark/link_normal_icon.png);
}
}

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