Compare commits

...

305 Commits

Author SHA1 Message Date
kuaifan
fadff146b4 build 2024-10-24 12:12:23 +08:00
kuaifan
01feacfe54 no message 2024-10-24 11:48:44 +08:00
kuaifan
d6ddc5ff88 perf: 优化人脸签到设置 2024-10-24 10:55:58 +08:00
kuaifan
287b6b396d perf: 优化消息搜索速度 2024-10-24 07:32:31 +08:00
kuaifan
b976f294f9 perf: 优化显示 2024-10-23 18:38:32 +08:00
kuaifan
dce48bd0cb fix: 周报默认内容已完成工作负责人不显示的情况 2024-10-23 16:31:53 +08:00
kuaifan
ab84235890 no message 2024-10-23 16:14:42 +08:00
kuaifan
7445ac3a39 perf: 优化图片压缩 2024-10-23 15:37:00 +08:00
kuaifan
f9ceb3e2d8 perf: 优化显示 2024-10-23 11:30:48 +08:00
kuaifan
8bb7b60055 perf: 优化显示 2024-10-23 10:56:02 +08:00
kuaifan
190211a467 no message 2024-10-23 00:06:20 +08:00
kuaifan
8a6868e811 perf: 优化显示 2024-10-22 22:37:12 +08:00
kuaifan
6aa868c8d8 fix: 无法清除计划时间 2024-10-22 21:52:21 +08:00
kuaifan
4dfa1c8efc fix: 选择时间起始不正确的问题 2024-10-22 21:32:58 +08:00
kuaifan
e2e7bc8d72 fix: 修复iOS日历无法正常显示的情况 2024-10-22 20:39:09 +08:00
kuaifan
a97d78bbf4 no message 2024-10-22 20:12:58 +08:00
kuaifan
22dbd288df perf: 优化cmd命令 2024-10-22 14:54:57 +08:00
kuaifan
4685cdcd3c fix: 签到信息预览错误 2024-10-22 14:51:02 +08:00
kuaifan
f792b3d983 build 2024-10-22 12:45:29 +08:00
kuaifan
adc94cef90 no message 2024-10-22 11:27:30 +08:00
kuaifan
e639cfbc2f perf: 优化显示效果 2024-10-22 11:27:24 +08:00
kuaifan
e520cd9020 build 2024-10-22 00:23:13 +08:00
kuaifan
daf8d15f45 perf: 升级onlyoffice 2024-10-21 22:40:50 +08:00
kuaifan
0e473ceacc no message 2024-10-21 20:19:04 +08:00
kuaifan
873bd0ed88 fix: 推送失败的情况 2024-10-21 19:05:24 +08:00
kuaifan
58b7853d63 perf: 优化人脸签到功能 2024-10-21 18:03:23 +08:00
kuaifan
2284788366 Merge commit '814a488801b328daf67f86c33ac422704303dceb' into pro
# Conflicts:
#	app/Http/Controllers/Api/SystemController.php
#	public/site/en/about.html
#	public/site/en/download.html
#	public/site/en/help.html
#	public/site/en/index.html
#	public/site/en/log.html
#	public/site/en/price.html
#	public/site/en/product.html
#	public/site/en/solutions.html
#	public/site/zh/about.html
#	public/site/zh/download.html
#	public/site/zh/help.html
#	public/site/zh/index.html
#	public/site/zh/log.html
#	public/site/zh/price.html
#	public/site/zh/product.html
#	public/site/zh/solutions.html
#	resources/mobile
2024-10-21 14:14:45 +08:00
kuaifan
d1766e52b6 Merge commit 'e3f5fb323ad00b6804acf265eec1fc5040d05a81' into pro 2024-10-21 13:33:44 +08:00
kuaifan
fdd5e36d19 no message 2024-10-21 12:53:03 +08:00
kuaifan
4fe4dc8c6e pref: 优化加载通讯录数量 2024-10-20 19:53:11 +08:00
kuaifan
a3202cbead no message 2024-10-20 18:39:18 +08:00
kuaifan
e8b03ae565 fix: 导出签到数据快速选择时间 2024-10-20 01:53:52 +08:00
kuaifan
829e3982d2 no message 2024-10-20 01:53:20 +08:00
kuaifan
07c5f586b0 no message 2024-10-19 23:49:28 +08:00
kuaifan
2ebaeb3453 no message 2024-10-19 22:13:44 +08:00
kuaifan
5660be12f6 no message 2024-10-19 17:15:19 +08:00
kuaifan
3cd00e1343 no message 2024-10-19 11:09:17 +08:00
kuaifan
f983146501 fix: 搜索区域无法回车搜索的问题 2024-10-18 22:10:48 +08:00
kuaifan
6cf64ce538 perf: 优化继续添加任务数据处理 2024-10-18 22:01:20 +08:00
kuaifan
47a7876505 fix: 未领任务提醒机器人无须加入项目 2024-10-18 22:00:15 +08:00
kuaifan
3f5ac55753 perf: 优化翻译 2024-10-18 21:58:55 +08:00
kuaifan
a33d95f2c1 perf: 更新gpt的一些模型 2024-10-18 15:44:14 +08:00
kuaifan
1128db184e no message 2024-10-17 14:03:51 +08:00
kuaifan
153fd6c569 perf: 优化消息组件 2024-10-17 13:06:55 +08:00
kuaifan
c9d002c1cd perf: 优化消息组件 2024-10-16 18:40:53 +08:00
kuaifan
e0a108eb2e perf: 优化消息组件 2024-10-16 14:49:33 +08:00
kuaifan
ae587950b9 perf: 优化消息组件 2024-10-15 21:23:42 +08:00
kuaifan
e956a03098 no message 2024-10-14 16:58:40 +08:00
kuaifan
1702aab538 no message 2024-10-14 16:24:47 +08:00
kuaifan
3c67b49d08 perf: 优化后端翻译 2024-10-11 16:49:45 +08:00
kuaifan
d58246b255 perf: 优化创建任务提示时间冲突的逻辑 2024-10-11 16:03:07 +08:00
yijixx
814a488801 docs: 更新docker-compose 2024-10-11 14:04:42 +08:00
yijixx
e029b39eb9 perf: 人脸打卡配置 2024-10-11 11:33:01 +08:00
yijixx
a8361299c7 perf: 签到设置保存 2024-10-11 11:14:13 +08:00
gwok
e3f5fb323a fix:修复导出签到数据中这个月和上个月时间显示不准确的问题 2024-10-08 11:24:50 +08:00
yijixx
be262c3a69 perf: 签到设备显示 2024-10-08 09:32:52 +08:00
kuaifan
a4525d4519 fix: 日历中总是显示时间相差一个月 2024-10-03 09:27:58 +08:00
yijixx
4f6034457f perf: 打卡标签页 2024-09-30 18:34:42 +08:00
yijixx
5413457b6b feat: 支持人脸打卡设备 2024-09-29 17:31:15 +08:00
gwokwong
977cf61b50 feat:推广页点击联系我们展示企业微信二维码 2024-09-24 17:12:26 +08:00
gwokwong
8c8c5b04d5 feat:新增推广页 2024-09-24 12:54:27 +08:00
kuaifan
620465d62a no message 2024-09-23 09:48:08 +03:00
kuaifan
a80e0d4c45 build 2024-09-23 01:37:55 +03:00
kuaifan
0ab6e6ca8d perf: 优化翻译 2024-09-23 01:34:06 +03:00
kuaifan
dcd41b4be2 perf: 优化时间组件 2024-09-22 23:19:03 +03:00
kuaifan
33cd9358c0 perf: 优化时间组件 2024-09-22 17:27:54 +03:00
kuaifan
51a3ad25d1 perf: 优化时间组件 2024-09-22 15:39:02 +03:00
kuaifan
f586938fe9 perf: 优化时间组件 2024-09-22 15:21:54 +03:00
kuaifan
912d229bdd perf: 优化时间组件 2024-09-22 11:16:50 +03:00
kuaifan
a93345afbd perf: 优化时间组件 2024-09-22 00:43:48 +03:00
kuaifan
a7bd0e0dac perf: 优化时间组件 2024-09-20 21:46:28 +03:00
kuaifan
e2fd37fe24 perf: 优化日历样式 2024-09-19 23:22:57 +03:00
kuaifan
6e6397fc91 no message 2024-09-19 15:55:55 +08:00
kuaifan
45c20dbed9 fix: 修复表情回应一处报错 2024-09-19 06:58:17 +08:00
kuaifan
594c19da03 perf: 手机端消息菜单居中 2024-09-19 06:47:16 +08:00
kuaifan
9251ccbb12 perf: 优化数据库外部访问方式 2024-09-19 06:33:17 +08:00
kuaifan
34305a1285 fix: 任务到期时间不变颜色 2024-09-19 00:42:57 +08:00
kuaifan
ccc60dfd77 perf: 优化消息选择文本 2024-09-18 23:17:18 +08:00
kuaifan
b7da689955 fix: 聊天提及内容错位的情况 2024-09-18 18:56:14 +08:00
kuaifan
0598a36b19 perf: 优化表情搜索 2024-09-18 18:55:17 +08:00
kuaifan
947e106f19 fix: 首次修改任务时间不提示时间冲突的问题 2024-09-18 17:38:14 +08:00
kuaifan
81957c9396 fix: 添加任务时不设置时间无须提示任务冲突 2024-09-18 17:13:24 +08:00
kuaifan
d54c86cec9 perf: 任务描述再次点击隐藏菜单 2024-09-18 13:24:24 +08:00
kuaifan
c17eca28fa fix: 负责人修改后不显示在仪表盘的情况 2024-09-17 00:02:26 +08:00
kuaifan
9a69f3b019 no message 2024-09-16 23:31:19 +08:00
kuaifan
c39fc80c02 fix: 添加任务选择今天时间无效的情况 2024-09-16 10:09:42 +03:00
kuaifan
b0eead121a perf: 优化工作包括模板 2024-09-16 09:43:41 +03:00
kuaifan
511b98ab5b perf: 仪表盘任务列表支持折叠 2024-09-16 08:51:26 +03:00
weifs
a69b01ecf5 fix: 修复url-token登录异常问题 2024-09-13 14:01:27 +08:00
kuaifan
a967493d77 perf: 新增修改任务时间权限 2024-09-13 06:41:20 +03:00
kuaifan
050c9702d8 perf: 优化子任务读取失败 2024-09-13 05:40:44 +03:00
spylecym
0d23b973de feat: 官网侧边导航按钮新增谷歌分析事件追踪 2024-09-06 16:44:16 +08:00
spylecym
fc3170369b feat: 官网侧边导航按钮新增谷歌分析事件追踪 2024-09-06 10:35:00 +08:00
kuaifan
647f7fdc7d build 2024-09-01 19:55:57 +08:00
kuaifan
8c3cd379a2 no message 2024-09-01 19:52:41 +08:00
wfs
cf9051412a fix: 修复任务可见性为非项目人员时项目负责人不可见的bug 2024-09-01 15:53:21 +08:00
wfs
6db0ff5647 Merge branch 'pro2' into pro
# Conflicts:
#	public/site/js/googleAds.js
#	public/site/js/googleAnalyze.js
2024-09-01 15:00:49 +08:00
spylecym
9ce127df86 feat: 网页右下角导航改为点击显示以及手机端点击拨打电话直接拨号 2024-08-26 17:24:28 +08:00
spylecym
20eec62fde feat: 网页右下角导航改为点击显示以及手机端点击拨打电话直接拨号 2024-08-22 11:30:30 +08:00
wfs
effc8ce43f feat: 更改审批版本 2024-08-14 01:10:22 +08:00
wfs
ced25e0cd2 perf: 1.优化审批流程-审批人审核过后自动通过 2. 优化审批评论图片可以左右滑动查看 2024-08-14 00:24:49 +08:00
wfs
72c70fe494 Merge branch 'pro2' into pro
# Conflicts:
#	public/site/en/about.html
#	public/site/en/cookie.html
#	public/site/en/download.html
#	public/site/en/help.html
#	public/site/en/index.html
#	public/site/en/log.html
#	public/site/en/price.html
#	public/site/en/privacy.html
#	public/site/en/product.html
#	public/site/en/solutions.html
#	public/site/zh/about.html
#	public/site/zh/cookie.html
#	public/site/zh/download.html
#	public/site/zh/help.html
#	public/site/zh/index.html
#	public/site/zh/log.html
#	public/site/zh/price.html
#	public/site/zh/privacy.html
#	public/site/zh/product.html
#	public/site/zh/solutions.html
#	resources/mobile
2024-08-13 21:39:57 +08:00
spylecym
dc062a44e1 fix: 修改谷歌分析以及谷歌推广文件命名 2024-08-13 15:06:45 +08:00
spylecym
dff22272b5 fix: 修改谷歌分析代码 2024-08-13 14:18:59 +08:00
spylecym
a0a1e03b53 fix: 修改谷歌分析代码 2024-08-13 11:51:47 +08:00
spylecym
3915c065fe feat: 官网新增谷歌分析代码 2024-08-12 10:06:44 +08:00
spylecym
dc71a779e0 fix: 删除打印 2024-08-12 10:06:44 +08:00
spylecym
b56ba93634 feat: 页面新增谷歌分析 2024-08-12 10:06:44 +08:00
spylecym
2926472f7d fix: 修改关于我们页面公司介绍文案 2024-08-12 10:06:44 +08:00
spylecym
a253d42f10 feat: 官网新增谷歌分析代码 2024-08-12 09:12:30 +08:00
spylecym
700d566255 fix: 删除打印 2024-08-09 16:42:26 +08:00
spylecym
fbbace90aa feat: 页面新增谷歌分析 2024-08-09 16:41:27 +08:00
kuaifan
6b5f7e780c build 2024-08-06 21:16:27 +08:00
kuaifan
79d4932bee perf: 优化聊天视频预览 2024-08-06 18:45:08 +08:00
kuaifan
e8af0f2ea6 fix: 无法下载大文件 2024-08-06 18:24:49 +08:00
kuaifan
f1ecf33ce7 perf: 优化打包下载 2024-08-06 13:23:28 +08:00
kuaifan
18eaf56ff9 perf: 支持上传mov、webm视频 2024-08-06 13:19:25 +08:00
spylecym
75f15ccc96 fix: 修改关于我们页面公司介绍文案 2024-06-13 16:51:27 +08:00
weifs
3f17e91f72 Merge branch 'pro' of github.com:hitosea/dootask into pro
# Conflicts:
#	resources/mobile
2024-06-06 09:37:01 +08:00
kuaifan
ee6eddf308 build 2024-06-04 22:12:51 +08:00
kuaifan
da84f15e9f no message 2024-06-04 21:08:10 +08:00
kuaifan
62f4d43bd9 perf: 通讯录菜单添加会议 2024-06-04 20:19:14 +08:00
kuaifan
376120b6d0 perf: 优化文件里预览图片 2024-06-04 20:11:48 +08:00
kuaifan
ff872c7dce perf: 优化消息描述 2024-06-04 19:50:12 +08:00
kuaifan
a834481d32 fix: 切换对话之后无法通过右键@ 2024-06-04 19:33:38 +08:00
kuaifan
4c5d3bd43e build 2024-06-03 19:39:37 +08:00
weifs
b737b841f5 Merge branch 'kuaifan-pro' into pro 2024-06-03 19:30:25 +08:00
weifs
0c5500edd4 Merge branch 'kuaifan-pro' into pro 2024-06-03 15:18:06 +08:00
weifs
990a40e4e4 feat: 操作人员离职对okr的移交处理 2024-06-03 15:14:19 +08:00
kuaifan
5eb2124b06 build 2024-06-03 14:07:14 +08:00
kuaifan
20d8180347 Merge commit '008040d96c73051fca6db388b35077a0ca7b5b5b' into pro 2024-06-03 14:00:10 +08:00
kuaifan
49203c15a7 perf: 优化视频播放 2024-06-03 11:32:12 +08:00
weifs
008040d96c fix: 如果项目没有流程,无法选择移动后的状态,也没办法确定移动 2024-06-03 11:13:01 +08:00
kuaifan
1a36044de2 no message 2024-06-02 10:56:18 +08:00
kuaifan
4886edc684 perf: 新增通讯录菜单 2024-06-02 10:52:59 +08:00
kuaifan
617fe902a4 perf: 优化快捷创建群组 2024-06-02 10:41:33 +08:00
kuaifan
78ad3468ae build 2024-06-01 17:34:55 +08:00
kuaifan
b58de926b2 no message 2024-06-01 17:24:07 +08:00
kuaifan
7c4c7eea9c perf: 优化回复消息自动@逻辑 2024-06-01 14:43:59 +08:00
kuaifan
eb7d93af87 perf: 甘特图兼容移动端 2024-06-01 10:19:35 +08:00
kuaifan
956b68a545 no message 2024-06-01 08:35:42 +08:00
kuaifan
79065a7675 build 2024-05-31 20:14:06 +08:00
kuaifan
d3514a0334 no message 2024-05-31 18:14:51 +08:00
kuaifan
bb163605af perf: 团队管理选择离职时根据离职时间排序 2024-05-31 13:44:51 +08:00
kuaifan
afcbd6af92 perf: 移动端支持快速编辑描述选择 2024-05-31 13:24:43 +08:00
kuaifan
13edea3449 perf: 优化离职数据移交 2024-05-31 12:38:37 +08:00
kuaifan
6cdcd4e0dc no message 2024-05-30 23:10:02 +08:00
kuaifan
efce884494 perf: 优化举报功能 2024-05-30 22:56:51 +08:00
kuaifan
dcffeded9a fix: 更新可见性后仍存在对话会话列表 2024-05-30 22:10:52 +08:00
kuaifan
e24b6806da perf: 优化网络重连后会话数据逻辑 2024-05-30 19:02:58 +08:00
kuaifan
bc7874a3a0 perf: 优化甘特图移动端交互 2024-05-30 18:27:54 +08:00
kuaifan
d348871b0c perf: 优化md编辑器 2024-05-30 14:50:13 +08:00
kuaifan
f9ee740a8c Merge commit '58ca285edf93b744a6eb358610b73f7e385aa5cf' into pro 2024-05-29 16:02:24 +08:00
kuaifan
c0a90ae89d fix: 消息输入框回复冲突 2024-05-21 15:13:25 +08:00
weifs
58ca285edf feat: 更新approve容器 2024-05-21 11:58:46 +08:00
weifs
325f8c0f7e Merge branch 'kuaifan-pro' into pro 2024-05-21 11:24:52 +08:00
zzw
829fe7e4ba fix:调整智谱清言描述信息 2024-05-15 14:54:54 +08:00
zzw
a6a18a0ee4 feat:添加智谱清言机器人 2024-05-15 14:22:17 +08:00
kuaifan
eda9eb08d5 no message 2024-05-12 13:16:39 +09:00
kuaifan
4625ae7548 build 2024-05-12 00:18:34 +09:00
kuaifan
186d3b0d79 perf: 优化查看任务附件菜单 2024-05-11 23:50:28 +09:00
kuaifan
7bf5805714 perf: 独立窗口未激活阅读逻辑 2024-05-11 23:09:33 +09:00
kuaifan
19604c46f0 no message 2024-05-11 22:49:47 +09:00
kuaifan
a77a32d64e perf: 新消息在会话列表时间与消息里不一致 2024-05-11 22:13:37 +09:00
kuaifan
53145f0ca2 no message 2024-05-11 12:35:52 +09:00
kuaifan
6880baa6a4 perf: 优化Android点击发送按钮效果 2024-05-11 12:34:27 +09:00
kuaifan
2bda6bf668 perf: 支持修改消息待办 2024-05-11 10:53:32 +09:00
kuaifan
80fe978454 no message 2024-05-11 09:13:26 +09:00
kuaifan
94a30ea940 perf: 延期任务时间支持按天 2024-05-11 00:40:59 +09:00
kuaifan
f9d1aa93c4 perf: 优化待办消息样式 2024-05-10 23:52:41 +09:00
kuaifan
d3bda0d869 perf: 优化移动端子任务列表显示 2024-05-10 23:26:12 +09:00
kuaifan
426fa63288 perf: 语音消息转文字 2024-05-10 22:23:45 +09:00
kuaifan
bf46a00937 perf: 更新语音消息插件 2024-05-10 11:53:14 +09:00
kuaifan
0fce0c2386 perf: 优化设置样式 2024-05-09 16:08:44 +09:00
kuaifan
77843ccdee build 2024-05-02 00:55:18 +08:00
kuaifan
b86edcfa96 build 2024-05-01 23:22:56 +08:00
kuaifan
5fb242024a Merge commit 'd7d8ee481e720624225511e102773d1f2fc68e41' into pro 2024-05-01 15:55:13 +08:00
kuaifan
abbfbb85e6 no message 2024-05-01 15:20:15 +08:00
kuaifan
37407cdbac perf: 优化转发消息数据显示 2024-05-01 15:17:58 +08:00
kuaifan
f1f96bda4e build 2024-05-01 14:18:57 +08:00
kuaifan
6f38c4efdd feat: 语音消息未阅读红点提示 2024-05-01 12:02:12 +08:00
kuaifan
e325698899 perf: ipad 发送消息后出现页面跳动的情况 2024-05-01 10:52:52 +08:00
kuaifan
ed36d622ec perf: 仪表盘隐藏未到开始时间的任务 2024-04-30 16:46:04 +08:00
kuaifan
dabe1376c3 perf: 优化查看任务修改历史 2024-04-30 16:39:45 +08:00
kuaifan
199fd4462e perf: 优化聊天工具栏样式 2024-04-30 14:50:57 +08:00
kuaifan
85a7776159 perf: 优化更新聊天中的待办 2024-04-30 13:37:47 +08:00
weifs
d7d8ee481e feat: 修复移动任务中选完成进行移动没有设置完成时间的bug 2024-04-29 20:59:24 +08:00
Pang
875da9fbe5 perf: 优化图标功能提示 2024-04-28 23:02:40 +08:00
spylecym
2bd8199d88 fix: 修复官网帮助中心英文页面头部导航缺失问题 2024-04-28 18:19:21 +08:00
spylecym
ca490f3e96 feat: 新增右侧底部导航 2024-04-28 18:14:35 +08:00
Pang
b81f2f0675 fix: 安装系统部分情况没有数据 2024-04-26 19:46:27 +08:00
weifs
aef23dda13 fix: 修复举报样式错乱 2024-04-26 09:59:18 +08:00
weifs
693fa46688 perf: 审批和任务通知优化 2024-04-25 11:24:30 +08:00
weifs
30676fb761 feat: 添加举报功能 2024-04-24 19:22:36 +08:00
weifs
ac6bdc07ec perf: 优化按钮没有对应类型,控制台报错 2024-04-24 11:34:31 +08:00
kuaifan
f6afdd6604 build 2024-04-23 16:28:14 +08:00
kuaifan
856037c3c9 fix: 任务描述保存图片失败 2024-04-23 16:21:01 +08:00
kuaifan
3203da411d 优化android自动编译 2024-04-23 15:57:44 +08:00
kuaifan
a6708a26a6 perf: 转发消息至群聊时支持@留言 2024-04-23 11:04:43 +08:00
kuaifan
053daa621b perf: 自动发布Android 2024-04-22 18:30:08 +08:00
Pang
a16f5fca07 build 2024-04-22 10:23:43 +08:00
Pang
cfdb6e2a93 fix: 上一版本导致的无法@ 2024-04-22 10:16:37 +08:00
Pang
73261da19b perf: 优化代码 2024-04-22 10:00:26 +08:00
Pang
71f48a4f7c perf: 优化查看文件历史 2024-04-22 09:39:19 +08:00
Pang
dbdb805269 perf: 支持查看任务描述修改历史 2024-04-22 09:38:39 +08:00
Pang
bd61b8c948 perf: 任务描述支持清单 2024-04-22 05:05:07 +08:00
kuaifan
5e6a21ddc5 docs 2024-04-20 21:04:08 +08:00
kuaifan
ccc8170ec7 build 2024-04-20 21:02:10 +08:00
kuaifan
d4bfbb81d8 fix: 修复关闭侧边回复窗口导致会话不正常的情况 2024-04-20 19:38:18 +08:00
kuaifan
a46ffa1089 perf: 消息内容支持待办列表 2024-04-20 19:13:38 +08:00
kuaifan
182e5a6974 perf: 优化自动识别发送消息类型 2024-04-20 16:04:51 +08:00
kuaifan
8ca021df6a perf: 聊天输入框粘贴格式优化 2024-04-20 16:04:22 +08:00
kuaifan
106c011f6b perf: 优化网络错误提示 2024-04-19 15:39:04 +08:00
kuaifan
76664c61c4 build 2024-04-18 12:21:32 +08:00
kuaifan
24839f960f fix: 解决 Unable to preventDefault inside passive event listener 报错 2024-04-18 12:16:42 +08:00
kuaifan
ce7d3f8475 fix: 截图粘贴出现两张图的情况 2024-04-18 12:15:50 +08:00
kuaifan
5e8a6af74c fix: 聊天输入中文过程跟placeholder内容叠加的问题 2024-04-18 12:15:23 +08:00
kuaifan
23a363aeea build 2024-04-17 23:59:29 +08:00
kuaifan
6cbf2bbada perf: 下载pdf使用自带浏览器 2024-04-17 10:21:35 +08:00
kuaifan
ee9cf0a6b6 perf: 优化消息加载中效果 2024-04-17 09:32:53 +08:00
Pang
8d39b4aa0d perf: 审批内容禁止转发 2024-04-17 08:10:08 +08:00
Pang
3c93ad18b2 perf: 滑动快捷表情选择 2024-04-17 07:46:43 +08:00
kuaifan
cc125cc292 perf: 优化聊天输入框 2024-04-17 00:34:12 +08:00
kuaifan
6823d87198 build 2024-04-11 11:52:36 +08:00
kuaifan
288e857321 perf: update chat editor 2024-04-11 11:51:10 +08:00
kuaifan
73d1950d97 fix: added non-passive event listener to a scroll-blocking 'touchstart' event 2024-04-11 11:50:07 +08:00
kuaifan
ee2b047e5d no message 2024-04-11 10:16:00 +08:00
kuaifan
ae83fce524 perf: 优化机器人回复 2024-04-11 10:12:50 +08:00
kuaifan
985c5ff54b perf: 优化android体验 2024-04-11 08:48:55 +08:00
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
917 changed files with 60696 additions and 78771 deletions

55
.github/workflows/publish-android.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: Publish Android
on:
push:
tags:
- 'v*'
jobs:
Build:
name: Build Android
runs-on: ubuntu-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 Js
run: |
git submodule init
git submodule update --remote "resources/mobile"
./cmd appbuild publish
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '11'
- name: Build Android
run: |
cd resources/mobile/platforms/android/eeuiApp
chmod +x ./gradlew
./gradlew assembleRelease --quiet
- name: Upload File
env:
DP_KEY: ${{ secrets.DP_KEY }}
run: |
node ./electron/build.js android-upload
- name: Release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
with:
files: |
resources/mobile/platforms/android/eeuiApp/app/build/outputs/apk/release/*.apk

View File

@@ -7,7 +7,8 @@ on:
jobs:
Build:
runs-on: macos-latest
name: Build Mac
runs-on: macos-12
environment: build
if: startsWith(github.event.ref, 'refs/tags/v')

View File

@@ -7,6 +7,7 @@ on:
jobs:
Build:
name: Build Windows
runs-on: windows-latest
environment: build

1
.gitignore vendored
View File

@@ -27,3 +27,4 @@ laravels-timer-process.pid
vars.yaml
laravels.conf
laravels.pid
README_LOCAL.md

View File

@@ -1,22 +1,30 @@
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
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/cell/css/main.css
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/web-apps/apps/spreadsheeteditor/main/resources/css/app.css
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/web-apps/vendor/requirejs/require.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/web-apps/apps/spreadsheeteditor/main/app.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/common/AllFonts.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/web-apps/vendor/xregexp/xregexp-all-min.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/web-apps/vendor/socketio/socket.io.min.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/cell/sdk-all-min.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/cell/sdk-all.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/common/libfont/engine/fonts.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/common/Charts/ChartStyles.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/common/libfont/engine/fonts.wasm
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/web-apps/apps/presentationeditor/main/resources/css/app.css
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/web-apps/apps/presentationeditor/main/app.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/slide/sdk-all-min.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/slide/sdk-all.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/slide/themes//themes.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/web-apps/apps/documenteditor/main/resources/css/app.css
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/web-apps/apps/documenteditor/main/app.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/word/sdk-all-min.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/sdkjs/word/sdk-all.js
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/fonts/081
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/fonts/059
office/8.2.0-ee166620e87db663ac1ba15af1e7df5f/fonts/066
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,394 @@
All notable changes to this project will be documented in this file.
## [0.38.91]
### Bug Fixes
- 周报默认内容已完成工作负责人不显示的情况
- 无法清除计划时间
- 选择时间起始不正确的问题
- 修复iOS日历无法正常显示的情况
- 签到信息预览错误
### Performance
- 优化人脸签到设置
- 优化消息搜索速度
- 优化显示
- 优化图片压缩
- 优化cmd命令
## [0.38.73]
### Performance
- 优化显示效果
## [0.38.70]
### Bug Fixes
- 推送失败的情况
- 导出签到数据快速选择时间
- 搜索区域无法回车搜索的问题
- 未领任务提醒机器人无须加入项目
- 日历中总是显示时间相差一个月
### Documentation
- 更新docker-compose
### Features
- 支持人脸打卡设备
### Performance
- 升级onlyoffice
- 优化人脸签到功能
- 优化加载通讯录数量
- 优化继续添加任务数据处理
- 优化翻译
- 更新gpt的一些模型
- 优化消息组件
- 优化后端翻译
- 优化创建任务提示时间冲突的逻辑
- 人脸打卡配置
- 签到设置保存
- 签到设备显示
- 打卡标签页
## [0.38.27]
### Bug Fixes
- 修复表情回应一处报错
- 任务到期时间不变颜色
- 聊天提及内容错位的情况
- 首次修改任务时间不提示时间冲突的问题
- 添加任务时不设置时间无须提示任务冲突
- 负责人修改后不显示在仪表盘的情况
- 添加任务选择今天时间无效的情况
- 修复url-token登录异常问题
### Features
- 官网侧边导航按钮新增谷歌分析事件追踪
### Performance
- 优化翻译
- 优化时间组件
- 优化日历样式
- 手机端消息菜单居中
- 优化数据库外部访问方式
- 优化消息选择文本
- 优化表情搜索
- 任务描述再次点击隐藏菜单
- 优化工作包括模板
- 仪表盘任务列表支持折叠
- 新增修改任务时间权限
- 优化子任务读取失败
## [0.37.98]
### Bug Fixes
- 修复任务可见性为非项目人员时项目负责人不可见的bug
- 修改谷歌分析以及谷歌推广文件命名
- 修改谷歌分析代码
- 删除打印
- 修改关于我们页面公司介绍文案
### Features
- 网页右下角导航改为点击显示以及手机端点击拨打电话直接拨号
- 更改审批版本
- 官网新增谷歌分析代码
- 页面新增谷歌分析
### Performance
- 1.优化审批流程-审批人审核过后自动通过 2. 优化审批评论图片可以左右滑动查看
## [0.37.76]
### Bug Fixes
- 无法下载大文件
- 修改关于我们页面公司介绍文案
### Performance
- 优化聊天视频预览
- 优化打包下载
- 支持上传mov、webm视频
## [0.37.71]
### Bug Fixes
- 切换对话之后无法通过右键@
### Performance
- 通讯录菜单添加会议
- 优化文件里预览图片
- 优化消息描述
## [0.37.65]
### Features
- 操作人员离职对okr的移交处理
## [0.37.62]
### Bug Fixes
- 如果项目没有流程,无法选择移动后的状态,也没办法确定移动
### Performance
- 优化视频播放
- 新增通讯录菜单
- 优化快捷创建群组
- 优化回复消息自动@逻辑
- 甘特图兼容移动端
## [0.37.50]
### Bug Fixes
- 更新可见性后仍存在对话会话列表
- 消息输入框回复冲突
### Features
- 更新approve容器
- 添加智谱清言机器人
### Performance
- 团队管理选择离职时根据离职时间排序
- 移动端支持快速编辑描述选择
- 优化离职数据移交
- 优化举报功能
- 优化网络重连后会话数据逻辑
- 优化甘特图移动端交互
- 优化md编辑器
## [0.37.32]
### Performance
- 优化查看任务附件菜单
- 独立窗口未激活阅读逻辑
- 新消息在会话列表时间与消息里不一致
- 优化Android点击发送按钮效果
- 支持修改消息待办
- 延期任务时间支持按天
- 优化待办消息样式
- 优化移动端子任务列表显示
- 语音消息转文字
- 更新语音消息插件
- 优化设置样式
## [0.37.17]
### Bug Fixes
- 修复官网帮助中心英文页面头部导航缺失问题
- 安装系统部分情况没有数据
- 修复举报样式错乱
### Features
- 语音消息未阅读红点提示
- 修复移动任务中选完成进行移动没有设置完成时间的bug
- 新增右侧底部导航
- 添加举报功能
### Performance
- 优化转发消息数据显示
- Ipad 发送消息后出现页面跳动的情况
- 仪表盘隐藏未到开始时间的任务
- 优化查看任务修改历史
- 优化聊天工具栏样式
- 优化更新聊天中的待办
- 优化图标功能提示
- 审批和任务通知优化
- 优化按钮没有对应类型,控制台报错
## [0.36.97]
### Bug Fixes
- 任务描述保存图片失败
### Performance
- 转发消息至群聊时支持@留言
- 自动发布Android
## [0.36.91]
### Bug Fixes
- 上一版本导致的无法@
### Performance
- 优化代码
- 优化查看文件历史
- 支持查看任务描述修改历史
- 任务描述支持清单
## [0.36.84]
### Bug Fixes
- 修复关闭侧边回复窗口导致会话不正常的情况
### Performance
- 消息内容支持待办列表
- 优化自动识别发送消息类型
- 聊天输入框粘贴格式优化
- 优化网络错误提示
## [0.36.78]
### Bug Fixes
- 解决 Unable to preventDefault inside passive event listener 报错
- 截图粘贴出现两张图的情况
- 聊天输入中文过程跟placeholder内容叠加的问题
## [0.36.75]
### Bug Fixes
- Added non-passive event listener to a scroll-blocking 'touchstart' event
### Performance
- 下载pdf使用自带浏览器
- 优化消息加载中效果
- 审批内容禁止转发
- 滑动快捷表情选择
- 优化聊天输入框
- Update chat editor
- 优化机器人回复
- 优化android体验
## [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
@@ -271,6 +659,12 @@ All notable changes to this project will be documented in this file.
- 优化任务修改
## [0.33.37]
### Bug Fixes
- 更新导致的小问题
## [0.33.34]
### Bug Fixes

View File

@@ -88,7 +88,7 @@ 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 mysql "your command" # To run a mysql command (backup: Backup database, recovery: Restore database)
./cmd mysql "your command" # To run a mysql command (backup: Backup database, recovery: Restore database, open: Open database external port access, close: Close database external port access)
```
### SSL configuration

View File

@@ -89,7 +89,7 @@ cd dootask
./cmd redis "your command" # 运行 redis 命令
./cmd composer "your command" # 运行 composer 命令
./cmd supervisorctl "your command" # 运行 supervisorctl 命令
./cmd mysql "your command" # 运行 mysql 命令 (backup: 备份数据库recovery: 还原数据库)
./cmd mysql "your command" # 运行 mysql 命令 (backup: 备份数据库recovery: 还原数据库open: 开启数据库外部端口访问close: 关闭数据库外部端口访问)
```
### SSL 配置

View File

@@ -3,6 +3,7 @@
namespace App\Events;
use App\Models\WebSocket;
use App\Module\Base;
use Cache;
use Hhxsv5\LaravelS\Swoole\Events\WorkerStartInterface;
use Swoole\Http\Server;
@@ -19,6 +20,11 @@ class WorkerStartEvent implements WorkerStartInterface
if (isset($server->startMsecTime) && Cache::get("swooleServerStartMsecTime") != $server->startMsecTime) {
Cache::forever("swooleServerStartMsecTime", $server->startMsecTime);
WebSocket::query()->delete();
//
$all = Base::json2array(Cache::get("User::online:all"));
foreach ($all as $userid) {
Cache::forget("User::online:" . $userid);
}
}
}
}

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);
}
}
@@ -262,7 +262,7 @@ class ApproveController extends AbstractController
$this->approveMsg('approve_reviewer', $dialog, $botUser, $val, $process, $pass);
}
// 发起人
if ($process['is_finished'] == true) {
if ($process['is_finished']) {
$dialog = WebSocketDialog::checkUserDialog($botUser, $process['start_user_id']);
if (!empty($dialog)) {
$this->approveMsg('approve_submitter', $dialog, $botUser, ['userid' => $data['userid']], $process, $pass);
@@ -284,7 +284,7 @@ class ApproveController extends AbstractController
}
// 抄送人
$notifier = $this->handleProcessNode($process, $task['step']);
$notifier = $this->handleProcessNode($process);
if ($notifier && $pass == 'pass') {
foreach ($notifier as $val) {
$dialog = WebSocketDialog::checkUserDialog($botUser, $val['target_id']);
@@ -899,7 +899,7 @@ class ApproveController extends AbstractController
3 => '拒绝',
4 => '撤回'
);
return isset($state_map[$state]) ? $state_map[$state] : '';
return $state_map[$state] ?? '';
}
/**
@@ -966,7 +966,6 @@ class ApproveController extends AbstractController
return $res;
}
// 审批机器人消息
public function approveMsg($type, $dialog, $botUser, $toUser, $process, $action = null)
{
@@ -983,45 +982,52 @@ 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);
$text = preg_replace("/\n\x20+/", "\n", $text);
$msg_action = null;
$msgAction = null;
$msgData = [
'type' => $type,
'action' => $action,
'is_finished' => $process['is_finished'],
'data' => $data
];
$msgData['title'] = match ($type) {
'approve_reviewer' => '待你审批',
'approve_notifier' => '审批通知',
'approve_comment_notifier' => '审批评论通知',
'approve_submitter' => '审批结果',
default => '不支持的指令',
};
if ($action == 'withdraw' || $action == 'pass' || $action == 'refuse') {
// 任务完成,给发起人发送消息
if ($type == 'approve_submitter' && $action != 'withdraw') {
return WebSocketDialogMsg::sendMsg($msg_action, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, true);
return WebSocketDialogMsg::sendMsg($msgAction, $dialog->id, 'template', $msgData, $botUser->userid, false, false, true);
}
// 查找最后一条消息msg_id
$msg_action = 'change-' . $toUser['msg_id'];
$msgAction = 'change-' . $toUser['msg_id'];
}
//
try {
$msg = WebSocketDialogMsg::sendMsg($msg_action, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, true);
// 关联信息
if ($action == 'start') {
$proc_msg = new ApproveProcMsg();
$proc_msg->proc_inst_id = $process['id'];
$proc_msg->msg_id = $msg['data']->id;
$proc_msg->userid = $toUser['userid'];
$proc_msg->save();
}
// 更新工作报告 未读数量
if ($type == 'approve_reviewer' && $toUser['userid']) {
$params = [
'userid' => [$toUser['userid'], User::auth()->userid()],
'msg' => [
'type' => 'approve',
'action' => 'unread',
'userid' => $toUser['userid'],
]
];
Task::deliver(new PushTask($params, false));
}
} catch (\Throwable $th) {
//throw $th;
$msg = WebSocketDialogMsg::sendMsg($msgAction, $dialog->id, 'template', $msgData, $process['start_user_id'], false, false, true);
// 关联信息
if ($action == 'start') {
$proc_msg = new ApproveProcMsg();
$proc_msg->proc_inst_id = $process['id'];
$proc_msg->msg_id = $msg['data']->id;
$proc_msg->userid = $toUser['userid'];
$proc_msg->save();
}
// 更新审批 未读数量
if ($type == 'approve_reviewer' && $toUser['userid']) {
$params = [
'userid' => [$toUser['userid'], User::auth()->userid()],
'msg' => [
'type' => 'approve',
'action' => 'unread',
'userid' => $toUser['userid'],
]
];
Task::deliver(new PushTask($params, false));
}
return true;
}
@@ -1125,7 +1131,7 @@ class ApproveController extends AbstractController
$ret = Ihttp::ihttp_get($this->flow_url . '/api/v1/workflow/process/getUserApprovalStatus?' . http_build_query($data));
$procdef = json_decode($ret['ret'] == 1 ? $ret['data'] : '{}', true);
if (isset($procdef['status']) && $procdef['status'] == 200) {
return Base::retSuccess('success', isset($procdef['data']["proc_def_name"]) ? $procdef['data']["proc_def_name"] : '');
return Base::retSuccess('success', $procdef['data']["proc_def_name"] ?? '');
}
return Base::retSuccess('success', '');
}

View File

@@ -0,0 +1,161 @@
<?php
namespace App\Http\Controllers\Api;
use Request;
use App\Models\User;
use App\Module\Base;
use App\Models\Complaint;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
/**
* @apiDefine dialog
*
* 投诉
*/
class ComplaintController extends AbstractController
{
/**
* @api {get} api/complaint/lists 01. 获取举报投诉列表
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName lists
*
* @apiParam {Number} [type] 类型
* @apiParam {Number} [status] 状态
*
* @apiParam {Number} [page] 当前页,默认:1
* @apiParam {Number} [pagesize] 每页显示数量,默认:50最大:100
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function lists()
{
$user = User::auth();
$user->identity('admin');
//
$type = intval(Request::input('type'));
$status = Request::input('status');
//
$complaints = Complaint::query()
->when($type, function($q) use($type) {
$q->where('type', $type);
})
->when($status != "", function($q) use($status) {
$q->where('status', $status);
})
->orderByDesc('id')
->paginate(Base::getPaginate(100, 50));
//
return Base::retSuccess('success', $complaints);
}
/**
* @api {get} api/complaint/submit 02. 举报投诉
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName submit
*
* @apiParam {Number} dialog_id 对话ID
* @apiParam {Number} type 类型
* @apiParam {String} reason 原因
* @apiParam {String} imgs 图片
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function submit()
{
$user = User::auth();
//
$dialog_id = intval(Request::input('dialog_id'));
$type = intval(Request::input('type'));
$reason = trim(Request::input('reason'));
$imgs = Request::input('imgs');
//
WebSocketDialog::checkDialog($dialog_id);
//
if (!$type) {
return Base::retError('请选择举报类型');
}
if (!$reason) {
return Base::retError('请填写举报原因');
}
//
$report_imgs = [];
if (!empty($imgs) && is_array($imgs)) {
foreach ($imgs as $img) {
$report_imgs[] = Base::unFillUrl($img['path']);
}
}
//
Complaint::createInstance([
'dialog_id' => $dialog_id,
'userid' => $user->userid,
'type' => $type,
'reason' => $reason,
'imgs' => $report_imgs,
])->save();
// 通知管理员
$botUser = User::botGetOrCreate('system-msg');
User::where("identity", "like", "%,admin,%")
->orderByDesc('line_at')
->take(10)
->get()
->each(function ($adminUser) use ($reason, $botUser) {
$dialog = WebSocketDialog::checkUserDialog($botUser, $adminUser->userid);
if ($dialog) {
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'title' => '收到新的举报信息',
'content' => "收到新的举报信息:{$reason} (请前往应用查看详情)"
], $botUser->userid);
}
});
//
return Base::retSuccess('success');
}
/**
* @api {get} api/complaint/action 03. 举报投诉 - 操作
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName action
*
* @apiParam {Number} id ID
* @apiParam {Number} type 类型
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function action()
{
$user = User::auth();
$user->identity('admin');
//
$id = intval(Request::input('id'));
$type = trim(Request::input('type'));
//
if ($type == 'handle') {
Complaint::whereId($id)->update([
"status" => 1
]);
}
if ($type == 'delete') {
Complaint::whereId($id)->delete();
}
//
return Base::retSuccess('success');
}
}

View File

@@ -11,6 +11,7 @@ use Carbon\Carbon;
use App\Models\File;
use App\Models\User;
use App\Module\Base;
use App\Module\Extranet;
use App\Module\TimeRange;
use App\Models\FileContent;
use App\Models\AbstractModel;
@@ -58,29 +59,30 @@ 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));
$data = WebSocketDialog::getDialogBeyond($user->userid, Base::newCarbon($unreadAt), Base::newCarbon($todoAt));
//
return Base::retSuccess('success', $data);
}
@@ -108,12 +110,12 @@ class DialogController extends AbstractController
return Base::retError('请输入搜索关键词');
}
// 搜索会话
$dialogs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.hide', '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,18 +147,26 @@ class DialogController extends AbstractController
}
// 搜索消息会话
if (count($list) < 20) {
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_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)
->where('m.key', 'LIKE', "%{$key}%")
->orderByDesc('m.id')
$msgs = WebSocketDialogMsg::select(['web_socket_dialog_msgs.*'])
->join('web_socket_dialog_msg_reads as r', 'r.msg_id', '=', 'web_socket_dialog_msgs.id')
->where('r.userid', $user->userid)
->where('r.live', 1)
->where('web_socket_dialog_msgs.key', 'LIKE', "%{$key}%")
->orderByDesc('r.msg_id')
->take(20 - count($list))
->get();
$msgs->transform(function (WebSocketDialog $item) use ($user) {
return $item->formatData($user->userid);
});
$list = array_merge($list, $msgs->toArray());
foreach ($msgs as $msg) {
$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', $msg->dialog_id)
->first();
if (empty($item)) {
continue;
}
$item->search_msg_id = $msg->id;
$item->last_msg = $msg;
$list[] = $item->formatData($user->userid);
}
}
//
return Base::retSuccess('success', $list);
@@ -178,7 +188,7 @@ class DialogController extends AbstractController
{
$user = User::auth();
// 搜索会话
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.hide', '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 +223,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.hide', '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 +259,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 +268,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);
}
@@ -333,7 +345,7 @@ class DialogController extends AbstractController
/**
* @api {get} api/dialog/hide 08. 会话隐藏
* @api {get} api/dialog/hide 09. 会话隐藏
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -366,7 +378,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/tel 09. 获取对方联系电话
* @api {get} api/dialog/tel 10. 获取对方联系电话
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -416,7 +428,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/open/user 10. 打开会话
* @api {get} api/dialog/open/user 11. 打开会话
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -447,7 +459,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/list 11. 获取消息列表
* @api {get} api/dialog/msg/list 12. 获取消息列表
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -494,6 +506,7 @@ class DialogController extends AbstractController
$builder = WebSocketDialogMsg::select([
'web_socket_dialog_msgs.*',
'read.mention',
'read.dot',
'read.read_at',
])->leftJoin('web_socket_dialog_msg_reads as read', function ($leftJoin) use ($user) {
$leftJoin
@@ -581,7 +594,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/latest 12. 获取最新消息列表
* @api {get} api/dialog/msg/latest 13. 获取最新消息列表
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -611,6 +624,7 @@ class DialogController extends AbstractController
$builder = WebSocketDialogMsg::select([
'web_socket_dialog_msgs.*',
'read.mention',
'read.dot',
'read.read_at',
])->leftJoin('web_socket_dialog_msg_reads as read', function ($leftJoin) use ($user) {
$leftJoin
@@ -646,7 +660,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/search 13. 搜索消息位置
* @api {get} api/dialog/msg/search 14. 搜索消息位置
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -681,7 +695,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/one 14. 获取单条消息
* @api {get} api/dialog/msg/one 15. 获取单条消息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -710,7 +724,40 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/read 15. 已读聊天消息
* @api {get} api/dialog/msg/dot 16. 聊天消息去除点
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName msg__dot
*
* @apiParam {Number} id 消息ID
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function msg__dot()
{
$user = User::auth();
//
$id = intval(Request::input('id'));
//
$msg = WebSocketDialogMsg::find($id);
if (empty($msg)) {
return Base::retError("消息不存在或已被删除");
}
//
WebSocketDialogMsgRead::whereMsgId($id)->whereUserid($user->userid)->change(['dot' => 0]);
//
return Base::retSuccess('success', [
'id' => $msg->id,
'dot' => 0,
]);
}
/**
* @api {get} api/dialog/msg/read 17. 已读聊天消息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -781,7 +828,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/unread 16. 获取未读消息数据
* @api {get} api/dialog/msg/unread 18. 获取未读消息数据
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -824,7 +871,73 @@ class DialogController extends AbstractController
}
/**
* @api {post} api/dialog/msg/stream 17. 通知成员监听消息
* @api {get} api/dialog/msg/checked 19. 设置消息checked
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName msg__checked
*
* @apiParam {Number} dialog_id 对话ID
* @apiParam {Number} msg_id 消息ID
* @apiParam {Number} index li 位置
* @apiParam {Number} checked 标记、取消标记
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
* @apiSuccessExample {json} data:
{
"id": 43,
"msg": {
// ....
},
}
*/
public function msg__checked()
{
$user = User::auth();
//
$dialog_id = intval(Request::input('dialog_id'));
$msg_id = intval(Request::input('msg_id'));
$index = intval(Request::input('index'));
$checked = intval(Request::input('checked'));
//
$dialogMsg = WebSocketDialogMsg::whereId($msg_id)->whereDialogId($dialog_id)->first();
if (empty($dialogMsg)) {
return Base::retError('消息不存在');
}
if ($dialogMsg->userid != $user->userid) {
return Base::retError('仅支持修改自己的消息');
}
if ($dialogMsg->type !== 'text') {
return Base::retError('仅支持文本消息');
}
//
$oldMsg = Base::json2array($dialogMsg->getRawOriginal('msg'));
$oldText = $oldMsg['text'] ?? '';
$newText = preg_replace_callback('/<li[^>]*>/i', function ($matches) use ($index, $checked) {
static $i = 0;
if ($i++ == $index) {
$checked = $checked ? 'checked' : 'unchecked';
return '<li data-list="' . $checked . '">';
}
return $matches[0];
}, $oldText);
//
$dialogMsg->updateInstance([
'msg' => array_merge($oldMsg, ['text' => $newText]),
]);
$dialogMsg->save();
//
return Base::retSuccess('success', [
'id' => $dialogMsg->id,
'msg' => $dialogMsg->msg,
]);
}
/**
* @api {post} api/dialog/msg/stream 20. 通知成员监听消息
*
* @apiDescription 通知指定会员EventSource监听流动消息
* @apiVersion 1.0.0
@@ -863,7 +976,7 @@ class DialogController extends AbstractController
}
/**
* @api {post} api/dialog/msg/sendtext 18. 发送消息
* @api {post} api/dialog/msg/sendtext 21. 发送消息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -872,6 +985,7 @@ class DialogController extends AbstractController
*
* @apiParam {Number} dialog_id 对话ID
* @apiParam {String} text 消息内容
* @apiParam {String} [key] 搜索关键词 (不设置根据内容自动生成)
* @apiParam {String} [text_type] 消息类型
* - html: HTML默认
* - md: MARKDOWN
@@ -899,6 +1013,7 @@ class DialogController extends AbstractController
$update_mark = !($user->bot && in_array(strtolower(trim(Request::input('update_mark'))), ['no', 'false', '0']));
$reply_id = intval(Request::input('reply_id'));
$text = trim(Request::input('text'));
$key = trim(Request::input('key'));
$text_type = strtolower(trim(Request::input('text_type')));
$silence = in_array(strtolower(trim(Request::input('silence'))), ['yes', 'true', '1']);
$markdown = in_array($text_type, ['md', 'markdown']);
@@ -951,20 +1066,23 @@ class DialogController extends AbstractController
'height' => -1,
'ext' => $ext,
];
$result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'file', $fileData, $user->userid, false, false, $silence);
if (empty($key)) {
$key = mb_substr(strip_tags($text), 0, 200);
}
$result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'file', $fileData, $user->userid, false, false, $silence, $key);
} else {
$msgData = ['text' => $text];
if ($markdown) {
$msgData['type'] = 'md';
}
$result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'text', $msgData, $user->userid, false, false, $silence);
$result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'text', $msgData, $user->userid, false, false, $silence, $key);
}
}
return $result;
}
/**
* @api {post} api/dialog/msg/sendrecord 19. 发送语音
* @api {post} api/dialog/msg/sendrecord 22. 发送语音
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1012,7 +1130,7 @@ class DialogController extends AbstractController
}
/**
* @api {post} api/dialog/msg/sendfile 20. 文件上传
* @api {post} api/dialog/msg/sendfile 23. 文件上传
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1044,7 +1162,7 @@ class DialogController extends AbstractController
}
/**
* @api {post} api/dialog/msg/sendfiles 21. 群发文件上传
* @api {post} api/dialog/msg/sendfiles 24. 群发文件上传
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1100,7 +1218,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/sendfileid 22. 通过文件ID发送文件
* @api {get} api/dialog/msg/sendfileid 25. 通过文件ID发送文件
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1170,7 +1288,7 @@ class DialogController extends AbstractController
}
/**
* @api {post} api/dialog/msg/sendanon 23. 发送匿名消息
* @api {post} api/dialog/msg/sendanon 26. 发送匿名消息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1219,11 +1337,14 @@ class DialogController extends AbstractController
if (empty($dialog)) {
return Base::retError('匿名机器人会话不存在');
}
return WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "<p>{$text}</p>"], $botUser->userid, true);
return WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'content' => $text,
], $botUser->userid, true);
}
/**
* @api {get} api/dialog/msg/readlist 24. 获取消息阅读情况
* @api {get} api/dialog/msg/readlist 27. 获取消息阅读情况
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1252,7 +1373,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/detail 25. 消息详情
* @api {get} api/dialog/msg/detail 28. 消息详情
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1300,7 +1421,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/download 26. 文件下载
* @api {get} api/dialog/msg/download 29. 文件下载
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1341,7 +1462,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/withdraw 27. 聊天消息撤回
* @api {get} api/dialog/msg/withdraw 30. 聊天消息撤回
*
* @apiDescription 消息撤回限制24小时内需要token身份
* @apiVersion 1.0.0
@@ -1367,7 +1488,59 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/mark 28. 消息标记操作
* @api {get} api/dialog/msg/voice2text 31. 语音消息转文字
*
* @apiDescription 将语音消息转文字需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName msg__voice2text
*
* @apiParam {Number} msg_id 消息ID
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function msg__voice2text()
{
$user = User::auth();
//
$msg_id = intval(Request::input("msg_id"));
$msg = WebSocketDialogMsg::whereId($msg_id)->first();
if (empty($msg)) {
return Base::retError("消息不存在或已被删除");
}
if ($msg->type !== 'record') {
return Base::retError("仅支持语音消息");
}
$msgData = Base::json2array($msg->getRawOriginal('msg'));
if ($msgData['text']) {
$textUserid = is_array($msgData['text_userid']) ? $msgData['text_userid'] : [];
if (!in_array($user->userid, $textUserid)) {
$textUserid[] = $user->userid;
$msg->updateInstance([
'msg' => array_merge($msgData, ['text_userid' => $textUserid]),
]);
$msg->save();
}
return Base::retSuccess("success", $msg);
}
WebSocketDialog::checkDialog($msg->dialog_id);
//
$res = Extranet::openAItranscriptions(public_path($msgData['path']));
if (Base::isError($res)) {
return $res;
}
//
$msg->updateInstance([
'msg' => array_merge($msgData, ['text' => $res['data'], 'text_userid' => [$user->userid]]),
]);
$msg->save();
return Base::retSuccess("success", $msg);
}
/**
* @api {get} api/dialog/msg/mark 32. 消息标记操作
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1431,7 +1604,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/silence 29. 消息免打扰
* @api {get} api/dialog/msg/silence 33. 消息免打扰
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1494,7 +1667,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/forward 30. 转发消息给
* @api {get} api/dialog/msg/forward 34. 转发消息给
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1535,7 +1708,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/emoji 31. emoji回复
* @api {get} api/dialog/msg/emoji 35. emoji回复
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1570,7 +1743,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/tag 32. 标注/取消标注
* @api {get} api/dialog/msg/tag 36. 标注/取消标注
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1599,7 +1772,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/todo 33. 设待办/取消待办
* @api {get} api/dialog/msg/todo 37. 设待办/取消待办
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1610,7 +1783,9 @@ class DialogController extends AbstractController
* @apiParam {String} type 设待办对象
* - all: 会话全部成员(默认)
* - user: 会话指定成员
* @apiParam {Array} userids 会员ID组type=user有效格式: [userid1, userid2, userid3]
* @apiParam {Array} userids 会员ID组
* - type=user 有效,格式: [userid1, userid2, userid3]
* - 可通过 type=user 及 userids:[] 一起使用来清除所有人的待办
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@@ -1618,31 +1793,29 @@ class DialogController extends AbstractController
*/
public function msg__todo()
{
Base::checkClientVersion('0.37.18');
$user = User::auth();
//
$msg_id = intval(Request::input("msg_id"));
$type = trim(Request::input("type", "all"));
$userids = Request::input('userids');
//
if ($type === 'user') {
if (empty($userids)) {
return Base::retError("选择指定成员");
}
} else {
$userids = [];
}
//
$msg = WebSocketDialogMsg::whereId($msg_id)->first();
if (empty($msg)) {
return Base::retError("消息不存在或已被删除");
}
WebSocketDialog::checkDialog($msg->dialog_id);
$dialog = WebSocketDialog::checkDialog($msg->dialog_id);
//
if ($type === 'all') {
$userids = $dialog->dialogUser->pluck('userid')->toArray();
} else {
$userids = is_array($userids) ? $userids : [];
}
return $msg->toggleTodoMsg($user->userid, $userids);
}
/**
* @api {get} api/dialog/msg/todolist 34. 获取消息待办情况
* @api {get} api/dialog/msg/todolist 38. 获取消息待办情况
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1672,7 +1845,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/done 35. 完成待办
* @api {get} api/dialog/msg/done 39. 完成待办
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1710,6 +1883,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,
]);
}
}
//
@@ -1719,7 +1898,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/color 36. 设置颜色
* @api {get} api/dialog/msg/color 40. 设置颜色
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1760,7 +1939,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/group/add 37. 新增群组
* @api {get} api/dialog/group/add 41. 新增群组
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1822,7 +2001,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/group/edit 38. 修改群组
* @api {get} api/dialog/group/edit 42. 修改群组
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1884,7 +2063,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/group/adduser 39. 添加群成员
* @api {get} api/dialog/group/adduser 43. 添加群成员
*
* @apiDescription 需要token身份
* - 有群主时:只有群主可以邀请
@@ -1920,7 +2099,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/group/deluser 40. 移出(退出)群成员
* @api {get} api/dialog/group/deluser 44. 移出(退出)群成员
*
* @apiDescription 需要token身份
* - 只有群主、邀请人可以踢人
@@ -1964,7 +2143,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/group/transfer 41. 转让群组
* @api {get} api/dialog/group/transfer 45. 转让群组
*
* @apiDescription 需要token身份
* - 只有群主且是个人类型群可以解散
@@ -2013,7 +2192,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/group/disband 42. 解散群组
* @api {get} api/dialog/group/disband 46. 解散群组
*
* @apiDescription 需要token身份
* - 只有群主且是个人类型群可以解散
@@ -2041,7 +2220,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/group/searchuser 43. 搜索个人群(仅限管理员)
* @api {get} api/dialog/group/searchuser 47. 搜索个人群(仅限管理员)
*
* @apiDescription 需要token身份用于创建部门搜索个人群组
* @apiVersion 1.0.0
@@ -2070,7 +2249,7 @@ class DialogController extends AbstractController
}
/**
* @api {post} api/dialog/okr/add 44. 创建OKR评论会话
* @api {post} api/dialog/okr/add 48. 创建OKR评论会话
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2109,7 +2288,7 @@ class DialogController extends AbstractController
}
/**
* @api {post} api/dialog/okr/push 45. 推送OKR相关信息
* @api {post} api/dialog/okr/push 49. 推送OKR相关信息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2145,7 +2324,7 @@ class DialogController extends AbstractController
}
/**
* @api {post} api/dialog/msg/wordchain 46. 发送接龙消息
* @api {post} api/dialog/msg/wordchain 50. 发送接龙消息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2168,12 +2347,12 @@ 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) {
@@ -2188,17 +2367,31 @@ class DialogController extends AbstractController
->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;
//
$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;
}
// 编辑更新
$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']];
}
return $result;
}, []);
$list = array_reverse(array_values($list));
}
$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) {
@@ -2209,6 +2402,7 @@ class DialogController extends AbstractController
'text' => $text,
'list' => $list,
'userid' => $user->userid,
'createid' => $createId,
'uuid' => $uuid,
];
return WebSocketDialogMsg::sendMsg(null, $dialog_id, 'word-chain', $msgData, $user->userid);
@@ -2216,7 +2410,7 @@ class DialogController extends AbstractController
}
/**
* @api {post} api/dialog/msg/vote 47. 发起投票
* @api {post} api/dialog/msg/vote 51. 发起投票
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2332,7 +2526,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/top 48. 置顶/取消置顶
* @api {get} api/dialog/msg/top 52. 置顶/取消置顶
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2392,7 +2586,7 @@ class DialogController extends AbstractController
}
/**
* @api {get} api/dialog/msg/topinfo 49. 获取置顶消息
* @api {get} api/dialog/msg/topinfo 53. 获取置顶消息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2417,4 +2611,28 @@ class DialogController extends AbstractController
//
return Base::retSuccess('success', $topMsg);
}
/**
* @api {get} api/dialog/sticker/search 54. 搜索在线表情
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName sticker__search
*
* @apiParam {String} key 关键词
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function sticker__search()
{
User::auth();
//
$key = trim(Request::input('key'));
return Base::retSuccess('success', [
'list' => Extranet::sticker($key)
]);
}
}

View File

@@ -12,6 +12,7 @@ use App\Models\FileLink;
use App\Models\FileUser;
use App\Models\User;
use App\Module\Base;
use App\Module\Doo;
use App\Module\Ihttp;
use Response;
use Session;
@@ -552,14 +553,14 @@ class FileController extends AbstractController
$tmpPath = "uploads/file/document/" . date("Ym") . "/" . $id . "/attached/";
Base::makeDir(public_path($tmpPath));
$tmpPath .= md5($text) . "." . $matchs[1][$key];
if (Base::saveContentImage(public_path($tmpPath), base64_decode($text))) {
if (Base::saveContentImage(public_path($tmpPath), base64_decode($text), 90)) {
$paramet = getimagesize(public_path($tmpPath));
$data['content'] = str_replace($matchs[0][$key], '<img src="' . Base::fillUrl($tmpPath) . '" original-width="' . $paramet[0] . '" original-height="' . $paramet[1] . '"', $data['content']);
$isRep = true;
}
}
$text = strip_tags($data['content']);
if ($isRep == true) {
if ($isRep) {
$content = Base::array2json($data);
}
}
@@ -1089,14 +1090,13 @@ class FileController extends AbstractController
]);
}
//
$text = "<b>文件下载打包已完成。</b>";
$text .= "\n\n";
$text .= "文件名:{$fileName}";
$text .= "\n";
$text .= "文件大小:".Base::twoFloat(filesize($zipPath) / 1024, true)."KB";
$text .= "\n";
$text .= '<a href="' . $fileUrl . '" target="_blank"><button type="button" class="ivu-btn ivu-btn-warning" style="margin-top: 10px;"><span>立即下载</span></button></a>';
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, true);
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'file_download',
'title' => '文件下载打包已完成',
'name' => $fileName,
'size' => filesize($zipPath),
'url' => $fileUrl,
], $botUser->userid, false, false, true);
});
return Base::retSuccess('success', [
'name' => $fileName,

View File

@@ -32,6 +32,7 @@ use App\Models\ProjectTaskUser;
use App\Models\WebSocketDialog;
use App\Exceptions\ApiException;
use App\Models\ProjectPermission;
use App\Models\ProjectTaskContent;
use App\Models\WebSocketDialogMsg;
use App\Module\BillMultipleExport;
use Illuminate\Support\Facades\DB;
@@ -1110,7 +1111,7 @@ class ProjectController extends AbstractController
/**
* @api {get} api/project/task/easylists 20. 任务列表-简单的
*
* @apiDescription 需要token身份
* @apiDescription 需要token身份,主要用于判断是否有时间冲突的任务
* @apiVersion 1.0.0
* @apiGroup project
* @apiName task__easylists
@@ -1129,23 +1130,24 @@ class ProjectController extends AbstractController
//
$taskid = trim(Request::input('taskid'));
$userid = Request::input('userid');
$timerange = Request::input('timerange');
$timerange = TimeRange::parse(Request::input('timerange'));
//
$list = ProjectTask::with(['taskUser'])
->select('projects.name as project_name', 'project_tasks.id', 'project_tasks.name', 'project_tasks.start_at', 'project_tasks.end_at')
->select([
'projects.name as project_name',
'project_tasks.id',
'project_tasks.name',
'project_tasks.start_at',
'project_tasks.end_at'
])
->join('projects','project_tasks.project_id','=','projects.id')
->leftJoin('project_task_users', function ($query) {
$query->on('project_tasks.id', '=', 'project_task_users.task_id')->where('project_task_users.owner', '=', 1);
})
->whereIn('project_task_users.userid', is_array($userid) ? $userid : explode(',', $userid) )
->when(!empty($timerange), function ($query) use ($timerange) {
if (!is_array($timerange)) {
$timerange = explode(',', $timerange);
}
if (Base::isDateOrTime($timerange[0]) && Base::isDateOrTime($timerange[1])) {
$query->where('project_tasks.start_at', '<=', Carbon::parse($timerange[1])->endOfDay());
$query->where('project_tasks.end_at', '>=', Carbon::parse($timerange[0])->startOfDay());
}
->when($timerange->isExist(), function ($query) use ($timerange) {
$query->where('project_tasks.start_at', '<=', $timerange->lastTime()->endOfDay());
$query->where('project_tasks.end_at', '>=', $timerange->firstTime()->startOfDay());
})
->when(!empty($taskid), function ($query) use ($taskid) {
$query->where('project_tasks.id', "!=", $taskid);
@@ -1227,8 +1229,11 @@ class ProjectController extends AbstractController
$headings[] = '状态';
$datas = [];
//
$text = '<b>导出任务统计已完成。</b>';
$text .= "\n\n";
$content = [];
$content[] = [
'content' => '导出任务统计已完成',
'style' => 'font-weight: bold;padding-bottom: 4px;',
];
//
$builder = ProjectTask::select(['project_tasks.*', 'project_task_users.userid as ownerid'])
->join('project_task_users', 'project_tasks.id', '=', 'project_task_users.task_id')
@@ -1327,8 +1332,15 @@ class ProjectController extends AbstractController
}
});
if (empty($datas)) {
$text .= '没有任何数据';
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, true);
$content[] = [
'content' => '没有任何数据',
'style' => 'color: #ff0000;',
];
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'title' => $content[0]['content'],
'content' => $content,
], $botUser->userid, false, false, true);
return;
}
//
@@ -1352,8 +1364,15 @@ class ProjectController extends AbstractController
$export = new BillMultipleExport($sheets);
$res = $export->store($filePath . "/" . $fileName);
if ($res != 1) {
$text .= "导出失败,{$fileName}";
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, true);
$content[] = [
'content' => "导出失败,{$fileName}",
'style' => 'color: #ff0000;',
];
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'title' => $content[0]['content'],
'content' => $content,
], $botUser->userid, false, false, true);
return;
}
//
@@ -1374,17 +1393,26 @@ class ProjectController extends AbstractController
]));
$fileUrl = Base::fillUrl('api/project/task/down?key=' . urlencode($base64));
Session::put('task::export:userid', $user->userid);
$text .= "文件名:{$fileName}";
$text .= "\n";
$text .= "文件大小:" . Base::twoFloat(filesize($zipPath) / 1024, true) . "KB";
$text .= "\n";
$text .= '<a href="' . $fileUrl . '" target="_blank"><button type="button" class="ivu-btn ivu-btn-warning" style="margin-top: 10px;"><span>立即下载</span></button></a>';
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'file_download',
'title' => '导出任务统计已完成',
'name' => $fileName,
'size' => filesize($zipPath),
'url' => $fileUrl,
], $botUser->userid, false, false, true);
} else {
$text .= '打包失败,请稍后再试...';
$content[] = [
'content' => "打包失败,请稍后再试...",
'style' => 'color: #ff0000;',
];
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'title' => $content[0]['content'],
'content' => $content,
], $botUser->userid, false, false, true);
}
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, true);
});
return Base::retSuccess('success', ['msg' => '正在打包,请留意系统消息。']);
return Base::retSuccess('success');
}
/**
@@ -1552,12 +1580,12 @@ class ProjectController extends AbstractController
// 项目可见性
$project_userid = ProjectUser::whereProjectId($task->project_id)->whereOwner(1)->value('userid'); // 项目负责人
if ($task->visibility != 1 && $user->userid != $project_userid) {
$taskUserids = ProjectTaskUser::whereTaskId($task_id)->pluck('userid')->toArray(); //任务负责人、协助人
$subTaskUserids = ProjectTaskUser::whereTaskPid($task_id)->pluck('userid')->toArray(); //子任务负责人、协助人
$visibleUserids = ProjectTaskVisibilityUser::whereTaskId($task_id)->pluck('userid')->toArray(); //可见人
$taskUserids = ProjectTaskUser::whereTaskId($task_id)->pluck('userid')->toArray(); //任务负责人、协助人
$subTaskUserids = ProjectTaskUser::whereTaskPid($task_id)->pluck('userid')->toArray(); //子任务负责人、协助人
$visibleUserids = ProjectTaskVisibilityUser::whereTaskId($task_id)->pluck('userid')->toArray(); //可见人
$visibleUserids = array_merge($taskUserids, $subTaskUserids, $visibleUserids);
if (!in_array($user->userid, $visibleUserids)) {
return Base::retError('无任务权限');
return Base::retError('无任务权限', ['task_id' => $task_id, 'force' => 1], -4002);
}
}
//
@@ -1576,7 +1604,8 @@ class ProjectController extends AbstractController
* @apiGroup project
* @apiName task__content
*
* @apiParam {Number} task_id 任务ID
* @apiParam {Number} task_id 任务ID
* @apiParam {Number} [history_id] 历史ID获取历史版本
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@@ -1587,9 +1616,19 @@ class ProjectController extends AbstractController
User::auth();
//
$task_id = intval(Request::input('task_id'));
$history_id = intval(Request::input('history_id'));
//
$task = ProjectTask::userTask($task_id, null);
//
if ($history_id > 0) {
$taskContent = ProjectTaskContent::whereTaskId($task->id)->whereId($history_id)->first();
if (empty($taskContent)) {
return Base::retError('历史版本不存在');
}
return Base::retSuccess('success', array_merge($taskContent->getContentInfo(), [
'name' => $task->name,
]));
}
if (empty($task->content)) {
return Base::retSuccess('success', json_decode('{}'));
}
@@ -1597,7 +1636,39 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/files 26. 获取任务文件列表
* @api {get} api/project/task/content_history 26. 获取任务详细历史描述
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup project
* @apiName task__content_history
*
* @apiParam {Number} task_id 任务ID
*
* @apiParam {Number} [page] 当前页,默认:1
* @apiParam {Number} [pagesize] 每页显示数量,默认:20最大:100
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function task__content_history()
{
User::auth();
//
$task_id = intval(Request::input('task_id'));
//
$task = ProjectTask::userTask($task_id, null);
//
$data = ProjectTaskContent::select(['id', 'task_id', 'desc', 'userid', 'created_at'])
->whereTaskId($task->id)
->orderByDesc('id')
->paginate(Base::getPaginate(100, 20));
return Base::retSuccess('success', $data);
}
/**
* @api {get} api/project/task/files 27. 获取任务文件列表
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1622,7 +1693,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/filedelete 27. 删除任务文件
* @api {get} api/project/task/filedelete 28. 删除任务文件
*
* @apiDescription 需要token身份项目、任务负责人
* @apiVersion 1.0.0
@@ -1648,7 +1719,7 @@ class ProjectController extends AbstractController
//
$task = ProjectTask::userTask($file->task_id);
//
ProjectPermission::userTaskPermission(Project::userProject($task->project_id), ProjectPermission::TASK_UPDATE, $task);
ProjectPermission::userTaskPermission(Project::userProject($task->project_id), ProjectPermission::TASK_REMOVE, $task);
//
$task->pushMsg('filedelete', $file);
$file->delete();
@@ -1657,7 +1728,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/filedetail 28. 获取任务文件详情
* @api {get} api/project/task/filedetail 29. 获取任务文件详情
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1701,7 +1772,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/filedown 29. 下载任务文件
* @api {get} api/project/task/filedown 30. 下载任务文件
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1748,7 +1819,7 @@ class ProjectController extends AbstractController
}
/**
* @api {post} api/project/task/add 30. 添加任务
* @api {post} api/project/task/add 31. 添加任务
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1834,7 +1905,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/addsub 31. 添加子任务
* @api {get} api/project/task/addsub 32. 添加子任务
*
* @apiDescription 需要token身份项目、任务负责人
* @apiVersion 1.0.0
@@ -1881,7 +1952,7 @@ class ProjectController extends AbstractController
}
/**
* @api {post} api/project/task/update 32. 修改任务、子任务
* @api {post} api/project/task/update 33. 修改任务、子任务
*
* @apiDescription 需要token身份项目、任务负责人
* @apiVersion 1.0.0
@@ -1896,6 +1967,7 @@ class ProjectController extends AbstractController
* @apiParam {String} [content] 任务详情(子任务不支持)
* @apiParam {String} [color] 背景色(子任务不支持)
* @apiParam {Array} [assist] 修改协助人员(子任务不支持)
* @apiParam {Number} [visibility] 修改可见性
* @apiParam {Array} [visibility_appointor] 修改可见性人员
*
* @apiParam {Number} [p_level] 优先级相关(子任务不支持)
@@ -1919,11 +1991,13 @@ class ProjectController extends AbstractController
$task = ProjectTask::userTask($task_id);
//
$project = Project::userProject($task->project_id);
if (Arr::exists($param, 'flow_item_id')) {
ProjectPermission::userTaskPermission($project, ProjectPermission::TASK_STATUS, $task);
}else{
ProjectPermission::userTaskPermission($project, ProjectPermission::TASK_UPDATE, $task);
$permissionKey = ProjectPermission::TASK_UPDATE;
if (Arr::exists($param, 'times')) {
$permissionKey = ProjectPermission::TASK_TIME;
} else if (Arr::exists($param, 'flow_item_id')) {
$permissionKey = ProjectPermission::TASK_STATUS;
}
ProjectPermission::userTaskPermission($project, $permissionKey, $task);
//
$taskUser = ProjectTaskUser::select(['userid', 'owner'])->whereTaskId($task_id)->get();
$owners = $taskUser->where('owner', 1)->pluck('userid')->toArray();
@@ -1945,7 +2019,7 @@ class ProjectController extends AbstractController
$task->pushMsgVisibleAdd($data);
}
if ($param['visibility_appointor']) {
$newVisibleUserIds = $param['visibility_appointor'] ?? [];
$newVisibleUserIds = is_array($param['visibility_appointor']) ? $param['visibility_appointor'] : [];
$deleteUserIds = array_diff($visible, $newVisibleUserIds, $subUserids);
$addUserIds = array_diff($newVisibleUserIds, $visible);
$task->pushMsgVisibleUpdate($data, $deleteUserIds, $addUserIds);
@@ -1987,7 +2061,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/dialog 33. 创建/获取聊天室
* @api {get} api/project/task/dialog 34. 创建/获取聊天室
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2036,7 +2110,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/archived 34. 归档任务
* @api {get} api/project/task/archived 35. 归档任务
*
* @apiDescription 需要token身份项目、任务负责人
* @apiVersion 1.0.0
@@ -2081,7 +2155,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/remove 35. 删除任务
* @api {get} api/project/task/remove 36. 删除任务
*
* @apiDescription 需要token身份项目、任务负责人
* @apiVersion 1.0.0
@@ -2119,7 +2193,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/resetfromlog 36. 根据日志重置任务
* @api {get} api/project/task/resetfromlog 37. 根据日志重置任务
*
* @apiDescription 需要token身份项目、任务负责人
* @apiVersion 1.0.0
@@ -2178,7 +2252,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/flow 37. 任务工作流信息
* @api {get} api/project/task/flow 38. 任务工作流信息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2267,7 +2341,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/task/move 38. 任务移动
* @api {get} api/project/task/move 39. 任务移动
*
* @apiDescription 需要token身份项目、任务负责人
* @apiVersion 1.0.0
@@ -2295,13 +2369,14 @@ class ProjectController extends AbstractController
$flow_item_id = intval(Request::input('flow_item_id'));
$owner = Request::input('owner', []);
$assist = Request::input('assist', []);
$completeAt = trim(Request::input('complete_at', ''));
//
$task = ProjectTask::userTask($task_id);
//
$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,14 +2385,18 @@ 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('任务状态不存在');
}
} else if (!$flow_item_id && !$completeAt) {
if (projectFlowItem::whereProjectId($project->id)->count() > 0) {
return Base::retError('请选择移动后状态', [], 102);
}
}
//
$task->moveTask($project_id, $column_id, $flow_item_id, $owner, $assist);
$task->moveTask($project_id, $column_id, $flow_item_id, $owner, $assist, $completeAt);
//
$task = ProjectTask::userTask($task_id);
//
@@ -2325,7 +2404,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/flow/list 39. 工作流列表
* @api {get} api/project/flow/list 40. 工作流列表
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2351,7 +2430,7 @@ class ProjectController extends AbstractController
}
/**
* @api {post} api/project/flow/save 40. 保存工作流
* @api {post} api/project/flow/save 41. 保存工作流
*
* @apiDescription 需要token身份项目负责人
* @apiVersion 1.0.0
@@ -2385,7 +2464,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/flow/delete 41. 删除工作流
* @api {get} api/project/flow/delete 42. 删除工作流
*
* @apiDescription 需要token身份项目负责人
* @apiVersion 1.0.0
@@ -2417,7 +2496,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/log/lists 42. 获取项目、任务日志
* @api {get} api/project/log/lists 43. 获取项目、任务日志
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2447,7 +2526,7 @@ class ProjectController extends AbstractController
$builder->whereTaskId($task->id);
} else {
$project = Project::userProject($project_id);
$builder->with(['projectTask:id,parent_id,name'])->whereProjectId($project->id);
$builder->with(['projectTask:id,parent_id,name'])->whereProjectId($project->id)->whereTaskOnly(0);
}
//
$list = $builder->orderByDesc('created_at')->paginate(Base::getPaginate(100, 20));
@@ -2463,6 +2542,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;
});
//
@@ -2470,7 +2564,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/top 43. 项目置顶
* @api {get} api/project/top 44. 项目置顶
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2500,7 +2594,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/permission 44. 获取项目权限设置
* @api {get} api/project/permission 45. 获取项目权限设置
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2526,7 +2620,7 @@ class ProjectController extends AbstractController
}
/**
* @api {get} api/project/permission/update 45. 项目权限设置
* @api {get} api/project/permission/update 46. 项目权限设置
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2560,8 +2654,9 @@ class ProjectController extends AbstractController
ProjectPermission::TASK_LIST_SORT,
ProjectPermission::TASK_ADD,
ProjectPermission::TASK_UPDATE,
ProjectPermission::TASK_REMOVE,
ProjectPermission::TASK_TIME,
ProjectPermission::TASK_STATUS,
ProjectPermission::TASK_REMOVE,
ProjectPermission::TASK_ARCHIVED,
ProjectPermission::TASK_MOVE,
]);

View File

@@ -65,7 +65,9 @@ class PublicController extends AbstractController
}
/**
* {post} 签到 - 路由器openwrt上报
* {post} 签到 - 上报
* - 1、路由器openwrt签到上报
* - 2、考勤机签到上报
*
* @apiParam {String} key
* @apiParam {String} mac 使用逗号分割多个
@@ -78,18 +80,30 @@ class PublicController extends AbstractController
$key = trim(Request::input('key'));
$mac = trim(Request::input('mac'));
$time = intval(Request::input('time'));
$type = trim(Request::input('type'));
//
$setting = Base::setting('checkinSetting');
if ($setting['open'] !== 'open') {
return 'function off';
}
if (!in_array('auto', $setting['modes'])) {
return 'mode off';
$alreadyTip = false;
if ($type === 'face') {
if (!in_array('face', $setting['modes'])) {
return 'mode off';
}
if ($key != $setting['face_key']) {
return 'key error';
}
$alreadyTip = $setting['face_retip'] === 'open';
} else {
if (!in_array('auto', $setting['modes'])) {
return 'mode off';
}
if ($key != $setting['key']) {
return 'key error';
}
}
if ($key != $setting['key']) {
return 'key error';
}
if ($error = UserBot::checkinBotCheckin($mac, $time)) {
if ($error = UserBot::checkinBotCheckin($mac, $time, $alreadyTip)) {
return $error;
}
return 'success';

View File

@@ -53,8 +53,8 @@ class ReportController extends AbstractController
$builder->whereType($keys['type']);
}
if (is_array($keys['created_at'])) {
if ($keys['created_at'][0] > 0) $builder->where('created_at', '>=', date('Y-m-d H:i:s', Base::dayTimeF($keys['created_at'][0])));
if ($keys['created_at'][1] > 0) $builder->where('created_at', '<=', date('Y-m-d H:i:s', Base::dayTimeE($keys['created_at'][1])));
if ($keys['created_at'][0] > 0) $builder->where('created_at', '>=', Base::newCarbon($keys['created_at'][0])->startOfDay());
if ($keys['created_at'][1] > 0) $builder->where('created_at', '<=', Base::newCarbon($keys['created_at'][1])->endOfDay());
}
}
$list = $builder->orderByDesc('created_at')->paginate(Base::getPaginate(50, 20));
@@ -99,14 +99,14 @@ class ReportController extends AbstractController
$builder->whereType($keys['type']);
}
if (is_array($keys['created_at'])) {
if ($keys['created_at'][0] > 0) $builder->where('created_at', '>=', date('Y-m-d H:i:s', Base::dayTimeF($keys['created_at'][0])));
if ($keys['created_at'][1] > 0) $builder->where('created_at', '<=', date('Y-m-d H:i:s', Base::dayTimeE($keys['created_at'][1])));
if ($keys['created_at'][0] > 0) $builder->where('created_at', '>=', Base::newCarbon($keys['created_at'][0])->startOfDay());
if ($keys['created_at'][1] > 0) $builder->where('created_at', '<=', Base::newCarbon($keys['created_at'][1])->endOfDay());
}
}
$list = $builder->orderByDesc('created_at')->paginate(Base::getPaginate(50, 20));
if ($list->items()) {
foreach ($list->items() as $item) {
$item->receive_time = ReportReceive::query()->whereRid($item["id"])->whereUserid($user->userid)->value("receive_time");
$item->receive_at = ReportReceive::query()->whereRid($item["id"])->whereUserid($user->userid)->value("receive_at");
}
}
return Base::retSuccess('success', $list);
@@ -174,7 +174,7 @@ class ReportController extends AbstractController
foreach ($input["receive"] as $userid) {
$input["receive_content"][] = [
"receive_time" => Carbon::now()->toDateTimeString(),
"receive_at" => Carbon::now()->toDateTimeString(),
"userid" => $userid,
"read" => 0,
];
@@ -259,6 +259,7 @@ class ReportController extends AbstractController
$offset = abs(intval(Request::input("offset", 0)));
$id = intval(Request::input("offset", 0));
$now_dt = trim(Request::input("date")) ? Carbon::parse(Request::input("date")) : Carbon::now();
// 获取开始时间
if ($type === Report::DAILY) {
$start_time = Carbon::today();
@@ -280,9 +281,11 @@ class ReportController extends AbstractController
$start_time->startOfWeek();
$end_time = Carbon::instance($start_time)->endOfWeek();
}
// 生成唯一标识
$sign = Report::generateSign($type, 0, Carbon::instance($start_time));
$one = Report::whereSign($sign)->whereType($type)->first();
// 如果已经提交了相关汇报
if ($one && $id > 0) {
return Base::retSuccess('success', [
@@ -293,8 +296,16 @@ class ReportController extends AbstractController
]);
}
// 表格头部
$labels = [
Doo::translate('项目'),
Doo::translate('任务'),
Doo::translate('负责人'),
Doo::translate('备注'),
];
// 已完成的任务
$completeContent = "";
$completeDatas = [];
$complete_task = ProjectTask::query()
->whereNotNull("complete_at")
->whereBetween("complete_at", [$start_time->toDateTimeString(), $end_time->toDateTimeString()])
@@ -306,15 +317,20 @@ class ReportController extends AbstractController
if ($complete_task->isNotEmpty()) {
foreach ($complete_task as $task) {
$complete_at = Carbon::parse($task->complete_at);
$pre = $type == Report::WEEKLY ? ('<span>[' . Doo::translate('周' . ['日', '一', '二', '三', '四', '五', '六'][$complete_at->dayOfWeek]) . ']</span>&nbsp;') : '';
$completeContent .= "<li>{$pre}[{$task->project->name}] {$task->name}</li>";
$remark = $type == Report::WEEKLY ? ('<div style="text-align:center">[' . Doo::translate('周' . ['日', '一', '二', '三', '四', '五', '六'][$complete_at->dayOfWeek]) . ']</div>') : '&nbsp;';
$completeDatas[] = [
$task->project->name,
$task->name,
$task->taskUser->where("owner", 1)->map(function ($item) {
return User::userid2nickname($item->userid);
})->implode(", "),
$remark,
];
}
} else {
$completeContent = '<li>&nbsp;</li>';
}
// 未完成的任务
$unfinishedContent = "";
$unfinishedDatas = [];
$unfinished_task = ProjectTask::query()
->join("projects", "projects.id", "=", "project_tasks.project_id")
->whereNull("projects.archived_at")
@@ -330,12 +346,18 @@ class ReportController extends AbstractController
if ($unfinished_task->isNotEmpty()) {
foreach ($unfinished_task as $task) {
empty($task->end_at) || $end_at = Carbon::parse($task->end_at);
$pre = (!empty($end_at) && $end_at->lt($now_dt)) ? '<span style="color:#ff0000;">[' . Doo::translate('超期') . ']</span>&nbsp;' : '';
$unfinishedContent .= "<li>{$pre}[{$task->project->name}] {$task->name}</li>";
$remark = (!empty($end_at) && $end_at->lt($now_dt)) ? '<div style="color:#ff0000;text-align:center">[' . Doo::translate('超期') . ']</div>' : '&nbsp;';
$unfinishedDatas[] = [
$task->project->name,
$task->name,
$task->taskUser->where("owner", 1)->map(function ($item) {
return User::userid2nickname($item->userid);
})->implode(", "),
$remark,
];
}
} else {
$unfinishedContent = '<li>&nbsp;</li>';
}
// 生成标题
if ($type === Report::WEEKLY) {
$title = $user->nickname . "的周报[" . $start_time->format("m/d") . "-" . $end_time->format("m/d") . "]";
@@ -343,22 +365,43 @@ class ReportController extends AbstractController
} else {
$title = $user->nickname . "的日报[" . $start_time->format("Y/m/d") . "]";
}
$title = Doo::translate($title);
// 生成内容
$content = '<h2>' . Doo::translate('已完成工作') . '</h2><ol>' .
$completeContent . '</ol><h2>' .
Doo::translate('未完成的工作') . '</h2><ol>' .
$unfinishedContent . '</ol>';
$contents = [];
$contents[] = '<h2>' . Doo::translate('已完成工作') . '</h2>';
$contents[] = view('report', [
'labels' => $labels,
'datas' => $completeDatas,
])->render();
$contents[] = '<p>&nbsp;</p>';
$contents[] = '<h2>' . Doo::translate('未完成的工作') . '</h2>';
$contents[] = view('report', [
'labels' => $labels,
'datas' => $unfinishedDatas,
])->render();
if ($type === Report::WEEKLY) {
$content .= "<h2>" . Doo::translate("下周拟定计划") . "[" . $start_time->addWeek()->format("m/d") . "-" . $end_time->addWeek()->format("m/d") . "]</h2><ol><li>&nbsp;</li></ol>";
$contents[] = '<p>&nbsp;</p>';
$contents[] = "<h2>" . Doo::translate("下周拟定计划") . "[" . $start_time->addWeek()->format("m/d") . "-" . $end_time->addWeek()->format("m/d") . "]</h2>";
$contents[] = view('report', [
'labels' => [
Doo::translate('计划描述'),
Doo::translate('计划时间'),
Doo::translate('负责人'),
],
'datas' => [],
])->render();
}
$data = [
"time" => $start_time->toDateTimeString(),
"sign" => $sign,
"title" => $title,
"content" => $content,
"complete_task" => $complete_task,
"unfinished_task" => $unfinished_task,
"content" => implode("", $contents),
];
if ($one) {
$data['id'] = $one->id;
}

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', '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']
* - save: 保存设置(参数:['reg', 'reg_identity', 'reg_invite', 'temp_account_alias', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'anon_message', 'voice2text', '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,11 +60,13 @@ class SystemController extends AbstractController
'reg',
'reg_identity',
'reg_invite',
'temp_account_alias',
'login_code',
'password_policy',
'project_invite',
'chat_information',
'anon_message',
'voice2text',
'e2e_message',
'auto_archived',
'archived_day',
@@ -92,6 +94,9 @@ class SystemController extends AbstractController
return Base::retError('自动归档时间不可大于100天');
}
}
if ($all['voice2text'] == 'open' && empty(Base::settingFind('aibotSetting', 'openai_key'))) {
return Base::retError('开启语音转文字功能需要在应用中开启 ChatGPT AI 机器人。');
}
$setting = Base::setting('system', Base::newTrim($all));
} else {
$setting = Base::setting('system');
@@ -99,18 +104,20 @@ 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['voice2text'] = $setting['voice2text'] ?: 'close';
$setting['e2e_message'] = $setting['e2e_message'] ?: 'close';
$setting['auto_archived'] = $setting['auto_archived'] ?: 'close';
$setting['archived_day'] = floatval($setting['archived_day']) ?: 7;
@@ -126,6 +133,8 @@ class SystemController extends AbstractController
$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_closeai'] = env("SERVER_CLOSEAI") ?: 'open';
$setting['server_timezone'] = config('app.timezone');
$setting['server_version'] = Base::getVersion();
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
@@ -202,7 +211,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 返回数据
@@ -222,12 +231,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 {
@@ -238,6 +249,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('{}'));
@@ -278,6 +291,8 @@ class SystemController extends AbstractController
'gemini_key',
'gemini_model',
'gemini_agency',
'zhipu_key',
'zhipu_model',
];
if ($type == 'save') {
@@ -292,35 +307,45 @@ class SystemController extends AbstractController
}
$backup = $setting;
$setting = Base::setting('aibotSetting', Base::newTrim($all));
$tempMsg = [
'type' => 'content',
'content' => '设置成功'
];
//
if ($backup['openai_key'] != $setting['openai_key']) {
$botUser = User::botGetOrCreate('ai-openai');
if ($botUser && $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid)) {
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "设置成功"], $botUser->userid, true, false, true);
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', $tempMsg, $botUser->userid, true, false, true);
}
}
if ($backup['claude_token'] != $setting['claude_token']) {
$botUser = User::botGetOrCreate('ai-claude');
if ($botUser && $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid)) {
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "设置成功"], $botUser->userid, true, false, true);
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', $tempMsg, $botUser->userid, true, false, true);
}
}
if ($backup['wenxin_key'] != $setting['wenxin_key']) {
$botUser = User::botGetOrCreate('ai-wenxin');
if ($botUser && $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid)) {
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "设置成功"], $botUser->userid, true, false, true);
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', $tempMsg, $botUser->userid, true, false, true);
}
}
if ($backup['qianwen_key'] != $setting['qianwen_key']) {
$botUser = User::botGetOrCreate('ai-qianwen');
if ($botUser && $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid)) {
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "设置成功"], $botUser->userid, true, false, true);
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', $tempMsg, $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);
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', $tempMsg, $botUser->userid, true, false, true);
}
}
if ($backup['zhipu_key'] != $setting['zhipu_key']) {
$botUser = User::botGetOrCreate('ai-zhipu');
if ($botUser && $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid)) {
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', $tempMsg, $botUser->userid, true, false, true);
}
}
}
@@ -329,6 +354,7 @@ class SystemController extends AbstractController
$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';
$setting['zhipu_model'] = $setting['zhipu_model'] ?: 'glm-4';
if (env("SYSTEM_SETTING") == 'disabled') {
foreach ($keys as $item) {
if (strlen($setting[$item]) > 12) {
@@ -373,6 +399,10 @@ class SystemController extends AbstractController
'remindin',
'remindexceed',
'edit',
'face_upload',
'face_remark',
'face_retip',
'manual_remark',
'modes',
'key',
])) {
@@ -381,8 +411,16 @@ class SystemController extends AbstractController
}
if ($all['open'] === 'close') {
$all['key'] = md5(Base::generatePassword(32));
$all['face_key'] = md5(Base::generatePassword(32));
} else {
$botUser = User::botGetOrCreate('check-in');
if (!$botUser) {
return Base::retError('创建签到机器人失败');
}
}
if ($all['modes']) {
$all['modes'] = array_intersect($all['modes'], ['auto', 'manual', 'location', 'face']);
}
$all['modes'] = array_intersect($all['modes'], ['auto', 'manual', 'location']);
$setting = Base::setting('checkinSetting', Base::newTrim($all));
} else {
$setting = Base::setting('checkinSetting');
@@ -392,8 +430,16 @@ class SystemController extends AbstractController
$setting['key'] = md5(Base::generatePassword(32));
Base::setting('checkinSetting', $setting);
}
if (empty($setting['face_key'])) {
$setting['face_key'] = md5(Base::generatePassword(32));
Base::setting('checkinSetting', $setting);
}
//
$setting['open'] = $setting['open'] ?: 'close';
$setting['face_upload'] = $setting['face_upload'] ?: 'close';
$setting['face_remark'] = $setting['face_remark'] ?: Doo::translate('考勤机');
$setting['face_retip'] = $setting['face_retip'] ?: 'open';
$setting['manual_remark'] = $setting['manual_remark'] ?: Doo::translate('手动签到');
$setting['time'] = $setting['time'] ? Base::json2array($setting['time']) : ['09:00', '18:00'];
$setting['advance'] = intval($setting['advance']) ?: 120;
$setting['delay'] = intval($setting['delay']) ?: 120;
@@ -844,7 +890,8 @@ class SystemController extends AbstractController
"image64" => $image64,
"path" => $path,
"fileName" => $fileName,
"scale" => $scale
"scale" => $scale,
"quality" => 85
]);
} else {
$data = Base::upload([
@@ -852,7 +899,8 @@ class SystemController extends AbstractController
"type" => 'image',
"path" => $path,
"fileName" => $fileName,
"scale" => $scale
"scale" => $scale,
"quality" => 100
]);
}
if (Base::isError($data)) {
@@ -988,6 +1036,7 @@ class SystemController extends AbstractController
"image64" => $image64,
"path" => $path,
"fileName" => $fileName,
"quality" => 85
]);
} else {
$data = Base::upload([
@@ -995,6 +1044,7 @@ class SystemController extends AbstractController
"type" => 'file',
"path" => $path,
"fileName" => $fileName,
"quality" => 100
]);
}
//
@@ -1067,7 +1117,7 @@ class SystemController extends AbstractController
} catch (\Throwable $e) {
// 一般是请求超时
if (str_contains($e->getMessage(), "Timed Out")) {
return Base::retError("language.TimedOut");
return Base::retError("邮件发送超时,请检查邮箱配置是否正确");
} elseif ($e->getCode() === 550) {
return Base::retError('邮件内容被拒绝,请检查邮箱是否开启接收功能');
} else {
@@ -1322,21 +1372,35 @@ class SystemController extends AbstractController
*/
public function prefetch()
{
$file = base_path('.prefetch');
if (!file_exists($file)) {
return [];
$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);
}
}
}
$version = Base::getVersion();
$content = file_get_contents($file);
$array = explode("\n", $content);
$array = array_values(array_filter($array));
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);
}, array_values(array_filter($array)));
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Http\Controllers\Api;
/**
* 测试
*/
class TestController extends AbstractController
{
}

View File

@@ -20,6 +20,7 @@ use App\Models\UmengAlias;
use App\Models\UserDelete;
use App\Models\UserTransfer;
use App\Models\AbstractModel;
use App\Models\UserCheckinFace;
use App\Models\UserCheckinMac;
use App\Models\UserDepartment;
use App\Models\WebSocketDialog;
@@ -28,6 +29,7 @@ use App\Models\WebSocketDialogMsg;
use Illuminate\Support\Facades\DB;
use App\Models\UserEmailVerification;
use App\Module\AgoraIO\AgoraTokenGenerator;
use Swoole\Coroutine;
/**
* @apiDefine users
@@ -582,7 +584,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 +617,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();
}
//
@@ -669,9 +671,13 @@ class UsersController extends AbstractController
* - all: 全部
* - 其他值: 非机器人(默认)
* - keys.department 部门ID0表示默认部门不赋值获取所有部门
* - keys.checkin_mac 签到mac地址get_checkin_mac=1时有效
* - keys.checkin_face 人脸图片get_checkin_data=1时有效
* - yes: 仅有人脸图片
* - no: 无人脸图片
* - all: 全部
* - keys.checkin_mac 签到mac地址get_checkin_data=1时有效
*
* @apiParam {Number} [get_checkin_mac] 获取签到mac地址
* @apiParam {Number} [get_checkin_data] 获取签到mac地址
* - 0: 不获取(默认)
* - 1: 获取
* @apiParam {Number} [page] 当前页,默认:1
@@ -688,7 +694,7 @@ class UsersController extends AbstractController
$builder = User::select(['*', 'nickname as nickname_original']);
//
$keys = Request::input('keys');
$getCheckinMac = intval(Request::input('get_checkin_mac')) === 1;
$getCheckinData = intval(Request::input('get_checkin_data')) === 1;
if (is_array($keys)) {
if ($keys['key']) {
if (str_contains($keys['key'], "@")) {
@@ -723,6 +729,7 @@ class UsersController extends AbstractController
}
}
if ($keys['disable'] === 'yes') {
$builder->orderByDesc('disable_at');
$builder->whereNotNull('disable_at');
} elseif ($keys['disable'] !== 'all') {
$builder->whereNull('disable_at');
@@ -755,10 +762,17 @@ class UsersController extends AbstractController
$builder->orderBy("is_principal","desc");
}
}
if ($getCheckinMac && isset($keys['checkin_mac'])) {
$builder->whereIn('userid', function ($query) use ($keys) {
$query->select('userid')->from('user_checkin_macs')->where("mac", "like", "%{$keys['checkin_mac']}%");
});
if ($getCheckinData) {
if (isset($keys['checkin_face'])) {
$builder->whereIn('userid', function ($query) use ($keys) {
$query->select('userid')->from('user_checkin_faces')->whereNotNull("faceimg");
});
}
if (isset($keys['checkin_mac'])) {
$builder->whereIn('userid', function ($query) use ($keys) {
$query->select('userid')->from('user_checkin_macs')->where("mac", "like", "%{$keys['checkin_mac']}%");
});
}
}
} else {
$builder->whereNull('disable_at');
@@ -766,11 +780,11 @@ class UsersController extends AbstractController
}
$list = $builder->orderByDesc('userid')->paginate(Base::getPaginate(50, 20));
//
if ($getCheckinMac) {
$list->transform(function (User $user) use ($getCheckinMac) {
if($getCheckinMac){
$user->checkin_macs = UserCheckinMac::select(['id', 'mac', 'remark'])->whereUserid($user->userid)->orderBy('id')->get();
}
if ($getCheckinData) {
$list->transform(function (User $user) {
$checkinFace = UserCheckinFace::select(['faceimg'])->whereUserid($user->userid)->first();
$user->checkin_face = $checkinFace ? Base::fillUrl($checkinFace->faceimg) : '';
$user->checkin_macs = UserCheckinMac::select(['id', 'mac', 'remark'])->whereUserid($user->userid)->orderBy('id')->get();
return $user;
});
}
@@ -793,6 +807,7 @@ class UsersController extends AbstractController
* - settemp 设为临时帐号
* - cleartemp 取消临时身份(取消临时帐号)
* - checkin_macs 修改自动签到mac地址需要参数 checkin_macs
* - checkin_face 修改签到人脸图片(需要参数 checkin_face
* - department 修改部门(需要参数 department
* - setdisable 设为离职(需要参数 disable_time、transfer_userid
* - cleardisable 取消离职
@@ -803,6 +818,7 @@ class UsersController extends AbstractController
* @apiParam {String} [nickname] 昵称
* @apiParam {String} [profession] 职位
* @apiParam {String} [checkin_macs] 自动签到mac地址
* @apiParam {String} [checkin_face] 人脸图片地址
* @apiParam {String} [department] 部门
* @apiParam {String} [disable_time] 离职时间
* @apiParam {String} [transfer_userid] 离职交接人
@@ -826,25 +842,30 @@ class UsersController extends AbstractController
}
$userInfo->checkSystem(1);
//
$msg = '修改成功';
$upArray = [];
$upLdap = [];
$transferUser = null;
switch ($type) {
case 'setadmin':
$msg = '设置成功';
$upArray['identity'] = array_diff($userInfo->identity, ['admin']);
$upArray['identity'][] = 'admin';
break;
case 'clearadmin':
$msg = '取消成功';
$upArray['identity'] = array_diff($userInfo->identity, ['admin']);
break;
case 'settemp':
$msg = '设置成功';
$upArray['identity'] = array_diff($userInfo->identity, ['temp']);
$upArray['identity'][] = 'temp';
break;
case 'cleartemp':
$msg = '取消成功';
$upArray['identity'] = array_diff($userInfo->identity, ['temp']);
break;
@@ -862,6 +883,11 @@ class UsersController extends AbstractController
}
return UserCheckinMac::saveMac($userInfo->userid, $array);
case 'checkin_face':
$faceimg = $data['checkin_face'] ? $data['checkin_face'] : '';
return UserCheckinFace::saveFace($userInfo->userid, $userInfo->nickname, $faceimg, "管理员上传");
case 'department':
if (!is_array($data['department'])) {
$data['department'] = [];
@@ -878,6 +904,7 @@ class UsersController extends AbstractController
break;
case 'setdisable':
$msg = '操作成功';
if ($userInfo->userid === $user->userid) {
return Base::retError('不能操作自己离职');
}
@@ -898,11 +925,13 @@ class UsersController extends AbstractController
break;
case 'cleardisable':
$msg = '操作成功';
$upArray['identity'] = array_diff($userInfo->identity, ['disable']);
$upArray['disable_at'] = null;
break;
case 'delete':
$msg = '删除成功';
if ($userInfo->userid === $user->userid) {
return Base::retError('不能删除自己');
}
@@ -1004,7 +1033,10 @@ class UsersController extends AbstractController
'new_userid' => $transferUser->userid,
]);
$userTransfer->save();
$userTransfer->start();
go(function () use ($userTransfer) {
Coroutine::sleep(0.1);
$userTransfer->start();
});
} elseif ($type === 'cleardisable') {
// 取消离职重新加入全员群组
if (Base::settingFind('system', 'all_group_autoin', 'yes') === 'yes') {
@@ -1015,7 +1047,7 @@ class UsersController extends AbstractController
});
}
//
return Base::retSuccess('修改成功', $userInfo);
return Base::retSuccess($msg, $userInfo);
}
/**
@@ -1101,10 +1133,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 +1175,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,9 +1195,14 @@ 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} 发起的会议";
$name = $name ?: Doo::translate("{$user?->nickname} 发起的会议");
$channel = "DooTask:" . substr(md5($meetingid . env("APP_KEY")), 16);
$meeting = Meeting::createInstance([
'meetingid' => $meetingid,
@@ -1180,9 +1224,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 +1257,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 +1286,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();
@@ -1618,8 +1662,14 @@ class UsersController extends AbstractController
$user = User::auth();
//
$list = UserCheckinMac::whereUserid($user->userid)->orderBy('id')->get();
$userface = UserCheckinFace::whereUserid($user->userid)->first();
$data = [
'list' => $list,
'faceimg' => $userface ? Base::fillUrl($userface->faceimg) : ''
];
//
return Base::retSuccess('success', $list);
return Base::retSuccess('success', $data);
}
/**
@@ -1630,7 +1680,11 @@ class UsersController extends AbstractController
* @apiGroup users
* @apiName checkin__save
*
* @apiParam {Array} list 优先级数据,格式:[{mac,remark}]
* @apiParam {String} type 类型
* - face: 人脸识别设置
* - mac: MAC设置
* @apiParam {String} faceimg 人脸图片地址
* @apiParam {Array} list 优先级数据,格式:[{mac,remark}]
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@@ -1644,30 +1698,53 @@ class UsersController extends AbstractController
if ($setting['open'] !== 'open') {
return Base::retError('此功能未开启,请联系管理员开启');
}
if ($setting['edit'] !== 'open') {
return Base::retError('未开放修改权限,请联系管理员');
}
//
$type = Request::input('type');
$list = Request::input('list');
$array = [];
if (empty($list) || !is_array($list)) {
return Base::retError('参数错误');
}
foreach ($list AS $item) {
$item = Base::newTrim($item);
if (Base::isMac($item['mac'])) {
$mac = strtoupper($item['mac']);
$array[$mac] = [
'mac' => $mac,
'remark' => substr($item['remark'], 0, 50),
];
}
}
if (count($array) > 3) {
return Base::retError('最多只能添加3个MAC地址');
$faceimg = Request::input('faceimg');
//
$data = [
'list' => $list,
'faceimg' => $faceimg
];
switch ($type) {
case 'face':
if ($setting['face_upload'] !== 'open') {
return Base::retError('未开放修改权限,请联系管理员');
}
UserCheckinFace::saveFace($user->userid, $user->nickname(), $faceimg, "用户上传");
break;
case 'mac':
if ($setting['edit'] !== 'open') {
return Base::retError('未开放修改权限,请联系管理员');
}
$array = [];
if (empty($list) || !is_array($list)) {
return Base::retError('参数错误');
}
foreach ($list as $item) {
$item = Base::newTrim($item);
if (Base::isMac($item['mac'])) {
$mac = strtoupper($item['mac']);
$array[$mac] = [
'mac' => $mac,
'remark' => substr($item['remark'], 0, 50),
];
}
}
if (count($array) > 3) {
return Base::retError('最多只能添加3个MAC地址');
}
$saveMacRes = UserCheckinMac::saveMac($user->userid, $array);
$data['list'] = $saveMacRes['data'];
break;
default:
return Base::retError('参数错误');
}
//
return UserCheckinMac::saveMac($user->userid, $array);
return Base::retSuccess('修改成功', $data);
}
/**
@@ -1963,7 +2040,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

@@ -10,18 +10,19 @@ 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;
@@ -165,6 +166,8 @@ class IndexController extends InvokeController
Task::deliver(new JokeSoupTask());
// 未领取任务通知
Task::deliver(new UnclaimedTaskRemindTask());
// 关闭会议室
Task::deliver(new CloseMeetingRoomTask());
return "success";
}
@@ -192,9 +195,10 @@ class IndexController extends InvokeController
$publishPath = "uploads/desktop/{$publishVersion}/";
$res = Base::upload([
"file" => Request::file('file'),
"type" => 'desktop',
"type" => 'publish',
"path" => $publishPath,
"fileName" => true
"fileName" => true,
"quality" => 100
]);
if (Base::isSuccess($res)) {
file_put_contents($latestFile, $publishVersion);
@@ -236,29 +240,6 @@ class IndexController extends InvokeController
];
}
//
$path = "uploads/android";
$dirPath = public_path($path);
$lists = Base::readDir($dirPath);
$apkFile = null;
foreach ($lists as $file) {
if (!str_ends_with($file, '.apk')) {
continue;
}
if ($apkFile && strtotime($apkFile['time']) > filemtime($file)) {
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' => $fileSize > 0 ? Base::readableBytes($fileSize) : 0,
'url' => Base::fillUrl($path . $fileName),
];
}
if ($apkFile) {
$files = array_merge([$apkFile], $files);
}
return view('desktop', ['version' => $name, 'files' => $files]);
}
// 下载

View File

@@ -2,10 +2,7 @@
namespace App\Http\Controllers;
use App\Models\User;
use App\Module\Base;
use App\Tasks\IhttpTask;
use Hhxsv5\LaravelS\Swoole\Task\Task;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
@@ -32,24 +29,7 @@ class InvokeController extends BaseController
$msg = "404 not found (" . str_replace("__", "/", $app) . ").";
return Base::ajaxError($msg);
}
// 使用websocket请求
$apiWebsocket = Request::header('Api-Websocket');
if ($apiWebsocket) {
$userid = User::userid();
if ($userid > 0) {
$url = 'http://127.0.0.1:' . env('LARAVELS_LISTEN_PORT') . Request::getRequestUri();
$task = new IhttpTask($url, Request::post(), [
'Content-Type' => Request::header('Content-Type'),
'language' => Request::header('language'),
'token' => Request::header('token'),
]);
$task->setApiWebsocket($apiWebsocket);
$task->setApiUserid($userid);
Task::deliver($task);
return Base::retSuccess('wait');
}
}
// 正常请求
//
$res = $this->__before($method, $action);
if ($res === true || Base::isSuccess($res)) {
return $this->$app();

View File

@@ -146,7 +146,7 @@ class LdapUser extends Model
$path = "uploads/user/ldap/";
$file = "{$path}{$user->userid}.jpeg";
Base::makeDir(public_path($path));
if (Base::saveContentImage(public_path($file), $userimg)) {
if (Base::saveContentImage(public_path($file), $userimg, 90)) {
$user->userimg = $file;
}
}

View File

@@ -34,6 +34,26 @@ class AbstractModel extends Model
const ID = 'id';
protected $dates = [
'top_at',
'last_at',
'start_at',
'end_at',
'archived_at',
'complete_at',
'loop_at',
'receive_at',
'line_at',
'disable_at',
'clear_at',
'read_at',
'done_at',
'created_at',
'updated_at',
'deleted_at',

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)

41
app/Models/Complaint.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
namespace App\Models;
/**
* App\Models\Complaint
*
* @property int $id
* @property int|null $dialog_id 对话ID
* @property int|null $userid 举报人id
* @property int|null $type 举报类型
* @property string|null $reason 举报原因
* @property string|null $imgs 举报图片
* @property int|null $status 状态 0待处理、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|Complaint newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Complaint newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Complaint query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|Complaint whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Complaint whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Complaint whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Complaint whereImgs($value)
* @method static \Illuminate\Database\Eloquent\Builder|Complaint whereReason($value)
* @method static \Illuminate\Database\Eloquent\Builder|Complaint whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder|Complaint whereType($value)
* @method static \Illuminate\Database\Eloquent\Builder|Complaint whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Complaint whereUserid($value)
* @mixin \Eloquent
*/
class Complaint extends AbstractModel
{
}

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)
@@ -239,14 +245,13 @@ class File extends AbstractModel
}
}
//
$setting = Base::setting('system');
$path = 'uploads/tmp/' . date("Ym") . '/';
$data = Base::upload([
"file" => Request::file('files'),
"type" => 'more',
"autoThumb" => false,
"path" => $path,
"size" => ($setting['file_upload_limit'] ?: 0) * 1024
"quality" => 100
]);
if (Base::isError($data)) {
throw new ApiException($data['msg']);

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)

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

@@ -16,11 +16,17 @@ use Illuminate\Support\Carbon;
* @property int|null $userid 创建人
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property string|null $end_at
* @property Carbon|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

@@ -22,7 +22,7 @@ use Request;
* @property int|null $personal 是否个人项目
* @property string|null $user_simple 成员总数|1,2,3
* @property int|null $dialog_id 聊天会话ID
* @property string|null $archived_at 归档时间
* @property \Illuminate\Support\Carbon|null $archived_at 归档时间
* @property int|null $archived_userid 归档会员
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
@@ -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,8 @@ 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 $task_only 仅任务日志0否1是
* @property int|null $userid 会员ID
* @property string|null $detail 详细信息
* @property array $record 记录数据
@@ -18,9 +19,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)
@@ -28,12 +35,16 @@ use App\Module\Base;
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereProjectId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereRecord($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereTaskId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereTaskOnly($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereUserid($value)
* @mixin \Eloquent
*/
class ProjectLog extends AbstractModel
{
protected $hidden = [
'task_only',
];
/**
* @param $value

View File

@@ -10,12 +10,18 @@ use App\Module\Base;
*
* @property int $id
* @property int|null $project_id 项目ID
* @property string $permissions 权限
* @property array $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)
@@ -32,6 +38,7 @@ class ProjectPermission extends AbstractModel
const TASK_LIST_SORT = 'task_list_sort'; // 列表排序
const TASK_ADD = 'task_add'; // 任务添加
const TASK_UPDATE = 'task_update'; // 任务更新
const TASK_TIME = 'task_time'; // 任务时间
const TASK_STATUS = 'task_status'; // 任务状态
const TASK_REMOVE = 'task_remove'; // 任务删除
const TASK_ARCHIVED = 'task_archived'; // 任务归档
@@ -63,7 +70,7 @@ class ProjectPermission extends AbstractModel
/**
* 权限
* @param $value
* @return string
* @return array
*/
public function getPermissionsAttribute($value)
{
@@ -104,7 +111,8 @@ class ProjectPermission extends AbstractModel
self::TASK_LIST_REMOVE => [self::PERMISSIONS['project_leader']],
self::TASK_LIST_SORT => $projectTaskList,
self::TASK_ADD => $projectTaskList,
self::TASK_UPDATE => [self::PERMISSIONS['project_leader'], self::PERMISSIONS['task_leader'], self::PERMISSIONS['task_assist']],
self::TASK_UPDATE => $taskUpdate = [self::PERMISSIONS['project_leader'], self::PERMISSIONS['task_leader'], self::PERMISSIONS['task_assist']],
self::TASK_TIME => $taskUpdate,
self::TASK_STATUS => $taskStatus = [self::PERMISSIONS['project_leader'], self::PERMISSIONS['task_leader']],
self::TASK_REMOVE => $taskStatus,
self::TASK_ARCHIVED => $taskStatus,
@@ -147,13 +155,14 @@ class ProjectPermission extends AbstractModel
$userid = User::userid();
$permissions = self::getPermission($project->id, $action);
switch ($action) {
// 任务添加,任务更新, 任务状态, 任务删除, 任务完成, 任务归档, 任务移动
// 任务添加,任务更新, 任务状态, 任务删除, 任务完成, 任务归档, 任务移动
case self::TASK_LIST_ADD:
case self::TASK_LIST_UPDATE:
case self::TASK_LIST_REMOVE:
case self::TASK_LIST_SORT:
case self::TASK_ADD:
case self::TASK_UPDATE:
case self::TASK_TIME:
case self::TASK_STATUS:
case self::TASK_REMOVE:
case self::TASK_ARCHIVED:

View File

@@ -9,8 +9,8 @@ use Carbon\Carbon;
use App\Module\Base;
use App\Tasks\PushTask;
use App\Exceptions\ApiException;
use App\Observers\ProjectTaskObserver;
use Hhxsv5\LaravelS\Swoole\Task\Task;
use App\Models\ProjectTaskVisibilityUser;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
@@ -26,12 +26,12 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property string|null $name 标题
* @property string|null $color 颜色
* @property string|null $desc 描述
* @property string|null $start_at 计划开始时间
* @property string|null $end_at 计划结束时间
* @property string|null $archived_at 归档时间
* @property \Illuminate\Support\Carbon|null $start_at 计划开始时间
* @property \Illuminate\Support\Carbon|null $end_at 计划结束时间
* @property \Illuminate\Support\Carbon|null $archived_at 归档时间
* @property int|null $archived_userid 归档会员
* @property int|null $archived_follow 跟随项目归档(项目取消归档时任务也取消归档)
* @property string|null $complete_at 完成时间
* @property \Illuminate\Support\Carbon|null $complete_at 完成时间
* @property int|null $userid 创建人
* @property int|null $visibility 任务可见性1-项目人员 2-任务人员 3-指定成员
* @property int|null $p_level 优先级
@@ -39,7 +39,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property string|null $p_color 优先级颜色
* @property int|null $sort 排序(ASC)
* @property string|null $loop 重复周期
* @property string|null $loop_at 下一次重复时间
* @property \Illuminate\Support\Carbon|null $loop_at 下一次重复时间
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
@@ -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)
@@ -346,6 +352,25 @@ class ProjectTask extends AbstractModel
return $query;
}
/**
* 生成描述
* @param $content
* @return string
*/
public static function generateDesc($content)
{
$content = preg_replace_callback('/<ul class="tox-checklist">(.+?)<\/ul>/is', function ($matches) {
return preg_replace_callback('/<li([^>]*)>(.+?)<\/li>/is', function ($m) {
if (str_contains($m[1], 'tox-checklist--checked')) {
return "<li{$m[1]}>[√]{$m[2]} </li>";
} else {
return "<li{$m[1]}>[ ]{$m[2]} </li>";
}
}, $matches[0]);
}, $content);
return Base::cutStr(strip_tags($content), 100, 0, "...");
}
/**
* 添加任务
* @param $data
@@ -368,7 +393,7 @@ class ProjectTask extends AbstractModel
$p_color = $data['p_color'];
$top = intval($data['top']);
$userid = User::userid();
$visibility = isset($data['visibility_appoint']) ? $data['visibility_appoint'] : $data['visibility'];
$visibility = $data['visibility_appoint'] ?? $data['visibility'];
$visibility_userids = $data['visibility_appointor'] ?: [];
//
if (ProjectTask::whereProjectId($project_id)
@@ -401,7 +426,7 @@ class ProjectTask extends AbstractModel
'visibility' => $visibility ?: 1
]);
if ($content) {
$task->desc = Base::getHtml($content, 100);
$task->desc = self::generateDesc($content);
}
// 标题
if (empty($name)) {
@@ -502,6 +527,8 @@ class ProjectTask extends AbstractModel
ProjectTaskContent::createInstance([
'project_id' => $task->project_id,
'task_id' => $task->id,
'userid' => $task->userid,
'desc' => $task->desc,
'content' => [
'url' => ProjectTaskContent::saveContent($task->id, $content)
],
@@ -713,13 +740,14 @@ class ProjectTask extends AbstractModel
}
}
$updateMarking['is_update_project'] = true;
$this->updated_at = Carbon::now();
$this->syncDialogUser();
}
// 可见性
if (Arr::exists($data, 'visibility') || Arr::exists($data, 'visibility_appointor')) {
if (Arr::exists($data, 'visibility')) {
ProjectTask::whereId($data['task_id'])->update(['visibility' => $data["visibility"]]);
ProjectTask::whereParentId($data['task_id'])->update(['visibility' => $data["visibility"]]);
$this->visibility = $data["visibility"];
ProjectTask::whereParentId($data['task_id'])->change(['visibility' => $data["visibility"]]);
}
ProjectTaskVisibilityUser::whereTaskId($data['task_id'])->delete();
if (Arr::exists($data, 'visibility_appointor')) {
@@ -732,6 +760,9 @@ class ProjectTask extends AbstractModel
])->save();
}
}
if (!Arr::exists($data, 'visibility')) {
ProjectTaskObserver::visibilityUpdate($this);
}
}
}
// 计划时间(原则:子任务时间在主任务时间内)
@@ -865,6 +896,7 @@ class ProjectTask extends AbstractModel
$row->delete();
}
}
$this->updated_at = Carbon::now();
$this->syncDialogUser();
}
// 背景色
@@ -888,15 +920,25 @@ class ProjectTask extends AbstractModel
}
// 内容
if (Arr::exists($data, 'content')) {
$logRecord = [];
$logContent = ProjectTaskContent::whereTaskId($this->id)->orderByDesc('id')->first();
if ($logContent) {
$logRecord['link'] = [
'title' => '查看历史',
'url' => 'single/task/content/' . $this->id . '?history_id=' . $logContent->id,
];
}
$this->desc = self::generateDesc($data['content']);
ProjectTaskContent::createInstance([
'project_id' => $this->project_id,
'task_id' => $this->id,
'userid' => User::userid(),
'desc' => $this->desc,
'content' => [
'url' => ProjectTaskContent::saveContent($this->id, $data['content'])
],
])->save();
$this->desc = Base::getHtml($data['content'], 100);
$this->addLog("修改{任务}详细描述");
$this->addLog("修改{任务}详细描述", $logRecord);
$updateMarking['is_update_content'] = true;
}
// 优先级
@@ -921,8 +963,6 @@ class ProjectTask extends AbstractModel
}
}
$this->save();
if ($this->start_at instanceof \DateTimeInterface) $this->start_at = $this->start_at->format('Y-m-d H:i:s');
if ($this->end_at instanceof \DateTimeInterface) $this->end_at = $this->end_at->format('Y-m-d H:i:s');
});
return true;
}
@@ -1238,7 +1278,9 @@ class ProjectTask extends AbstractModel
// 标记已完成
if ($this->parent_id == 0) {
if (self::whereParentId($this->id)->whereCompleteAt(null)->exists()) {
throw new ApiException('子任务未完成');
throw new ApiException('子任务未完成', [
'task_id' => $this->id
], -4004);
}
}
if (!$this->hasOwner()) {
@@ -1373,7 +1415,7 @@ class ProjectTask extends AbstractModel
* @param int $userid
* @return ProjectLog
*/
public function addLog($detail, $record = [], $userid = 0)
public function addLog($detail, $record = [], $userid = 0, $taskOnly = 0)
{
$detail = str_replace("{任务}", $this->parent_id ? "子任务" : "任务", $detail);
$array = [
@@ -1389,6 +1431,9 @@ class ProjectTask extends AbstractModel
if ($record) {
$array['record'] = $record;
}
if ($taskOnly) {
$array['task_only'] = $taskOnly;
}
$log = ProjectLog::createInstance($array);
$log->save();
return $log;
@@ -1566,14 +1611,18 @@ class ProjectTask extends AbstractModel
'dialog_id' => $this->dialog_id,
];
//
$projectOwnerids = ProjectUser::whereProjectId($this->project_id)->whereOwner(1)->pluck('userid')->toArray(); // 项目负责人
//
$array = [];
if (empty($userids)) {
// 默认 项目成员 与 项目负责人,任务负责人、协助人的差集
$projectUserids = ProjectUser::whereProjectId($this->project_id)->pluck('userid')->toArray(); // 项目成员
$projectOwner = ProjectUser::whereProjectId($this->project_id)->whereOwner(1)->pluck('userid')->toArray(); // 项目负责人
$taskOwnerAndAssists = ProjectTaskUser::select(['userid', 'owner'])->whereIn('owner', [0, 1])->whereTaskId($this->id)->pluck('userid')->toArray();
$subUserids = ProjectTaskUser::whereTaskPid($this->id)->pluck('userid')->toArray();
$userids = array_diff($projectUserids, $projectOwner, $taskOwnerAndAssists, $subUserids);
$userids = array_diff($projectUserids, $projectOwnerids, $taskOwnerAndAssists, $subUserids);
} else {
// 保证项目负责人都能看到
$userids = array_diff($userids, $projectOwnerids);
}
//
$array[] = [
@@ -1630,19 +1679,20 @@ class ProjectTask extends AbstractModel
if (empty($receivers)) {
return;
}
//
$userid = User::userid();
//
$botUser = User::botGetOrCreate('task-alert');
if (empty($botUser)) {
return;
}
$dataId = $this->parent_id ?: $this->id;
$taskHtml = "<span class=\"mention task\" data-id=\"{$dataId}\">#{$this->name}</span>";
$text = match ($type) {
1 => "您的任务 {$taskHtml} 即将超时",
2 => "您的任务 {$taskHtml} 已经超时",
3 => "您的任务 {$taskHtml} 时间已修改",
default => "您有一个新任务 {$taskHtml}",
1 => "(*)即将超时",
2 => "(*)已经超时",
3 => "(*)时间已修改",
default => "您有一个新任务",
};
/** @var User $user */
@@ -1660,62 +1710,82 @@ class ProjectTask extends AbstractModel
$dialog = WebSocketDialog::checkUserDialog($botUser, $receiver->userid);
if ($dialog) {
ProjectTaskPushLog::createInstance($data)->save();
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', [
'text' => str_replace("您的任务", $replace, $text) . $suffix
], $botUser->userid);
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'task_list',
'title' => str_replace("(*)", $replace, $text) . $suffix,
'list' => [
[
'id' => $dataId,
'name' => $this->name,
]
],
], in_array($type, [0, 3]) ? $userid : $botUser->userid);
}
}
}
/**
* 移动任务
* @param int $project_id
* @param int $column_id
* @param int $projectId
* @param int $columnId
* @param int $flowItemId
* @param array $owner
* @param array $assist
* @param string $completeAt
* @return bool
*/
public function moveTask(int $projectId, int $columnId,int $flowItemId = 0,array $owner = [], array $assist = [])
public function moveTask(int $projectId, int $columnId,int $flowItemId = 0,array $owner = [], array $assist = [], string $completeAt='')
{
AbstractModel::transaction(function () use($projectId, $columnId, $flowItemId, $owner, $assist) {
AbstractModel::transaction(function () use ($projectId, $columnId, $flowItemId, $owner, $assist, $completeAt) {
$newTaskUser = array_merge($owner, $assist);
//
$this->project_id = $projectId;
$this->column_id = $columnId;
$this->flow_item_id = $flowItemId;
// 任务内容
if($this->content){
if ($this->content) {
$this->content->project_id = $projectId;
$this->content->save();
}
// 任务文件
foreach ($this->taskFile as $taskFile){
foreach ($this->taskFile as $taskFile) {
$taskFile->project_id = $projectId;
$taskFile->save();
}
// 任务标签
foreach ($this->taskTag as $taskTag){
foreach ($this->taskTag as $taskTag) {
$taskTag->project_id = $projectId;
$taskTag->save();
}
// 任务用户
$this->updateTask(['owner' => $owner]);
$this->updateTask(['assist' => $assist]);
foreach ($this->taskUser as $taskUser){
if( in_array($taskUser->id, $newTaskUser) ){
$this->updateTask([
'owner' => $owner,
'assist' => $assist
]);
foreach ($this->taskUser as $taskUser) {
if (in_array($taskUser->id, $newTaskUser)) {
$taskUser->project_id = $projectId;
$taskUser->save();
}
}
//
if($flowItemId){
if ($flowItemId) {
$flowItem = projectFlowItem::whereProjectId($projectId)->whereId($flowItemId)->first();
$this->flow_item_id = $flowItemId;
$this->flow_item_name = $flowItem->status . "|" . $flowItem->name;
}else{
if ($flowItem->status == 'end') {
$this->completeTask(Carbon::now(), $flowItem->name);
} else {
$this->completeTask(null);
}
} else {
$this->flow_item_id = 0;
$this->flow_item_name = '';
}
//
if ($completeAt) {
$this->complete_at = $completeAt;
}
//
$this->save();
//
$this->addLog("移动{任务}");

View File

@@ -11,24 +11,33 @@ use App\Exceptions\ApiException;
* @property int $id
* @property int|null $project_id 项目ID
* @property int|null $task_id 任务ID
* @property int|null $userid 用户ID
* @property string|null $desc 内容描述
* @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 whereDesc($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent whereProjectId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent whereTaskId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskContent whereUserid($value)
* @mixin \Eloquent
*/
class ProjectTaskContent extends AbstractModel
{
protected $hidden = [
'created_at',
'updated_at',
];
@@ -70,7 +79,7 @@ class ProjectTaskContent extends AbstractModel
$tmpPath = $path . 'attached/';
Base::makeDir(public_path($tmpPath));
$tmpPath .= md5($text) . "." . $matchs[1][$key];
if (Base::saveContentImage(public_path($tmpPath), base64_decode($text))) {
if (Base::saveContentImage(public_path($tmpPath), base64_decode($text), 90)) {
$paramet = getimagesize(public_path($tmpPath));
$content = str_replace($matchs[0][$key], '<img src="{{RemoteURL}}' . $tmpPath . '" original-width="' . $paramet[0] . '" original-height="' . $paramet[1] . '"', $content);
}

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)
@@ -46,7 +52,7 @@ class ProjectTaskUser extends AbstractModel
*/
public static function transfer($originalUserid, $newUserid)
{
self::whereUserid($originalUserid)->chunk(100, function ($list) use ($originalUserid, $newUserid) {
self::whereUserid($originalUserid)->chunkById(100, function ($list) use ($originalUserid, $newUserid) {
$tastIds = [];
/** @var self $item */
foreach ($list as $item) {
@@ -62,7 +68,7 @@ class ProjectTaskUser extends AbstractModel
$item->save();
}
if ($item->projectTask) {
$item->projectTask->addLog("移交{任务}身份", ['userid' => [$originalUserid, ' => ', $newUserid]]);
$item->projectTask->addLog("移交{任务}身份", ['userid' => [$originalUserid, ' => ', $newUserid]], 0, 1);
if (!in_array($item->task_pid, $tastIds)) {
$tastIds[] = $item->task_pid;
$item->projectTask->syncDialogUser();

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

@@ -9,13 +9,19 @@ namespace App\Models;
* @property int|null $project_id 项目ID
* @property int|null $userid 成员ID
* @property int|null $owner 是否负责人
* @property string|null $top_at 置顶时间
* @property \Illuminate\Support\Carbon|null $top_at 置顶时间
* @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)
@@ -68,7 +74,7 @@ class Report extends AbstractModel
public function receivesUser(): BelongsToMany
{
return $this->belongsToMany(User::class, ReportReceive::class, "rid", "userid")
->withPivot("receive_time", "read");
->withPivot("receive_at", "read");
}
public function sendUser()

View File

@@ -10,15 +10,21 @@ use Illuminate\Database\Eloquent\Model;
*
* @property int $id
* @property int $rid
* @property string|null $receive_time 接收时间
* @property \Illuminate\Support\Carbon|null $receive_at 接收时间
* @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)
* @method static \Illuminate\Database\Eloquent\Builder|ReportReceive whereReceiveAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ReportReceive whereRid($value)
* @method static \Illuminate\Database\Eloquent\Builder|ReportReceive whereUserid($value)
* @mixin \Eloquent
@@ -32,7 +38,7 @@ class ReportReceive extends AbstractModel
protected $fillable = [
"rid",
"receive_time",
"receive_at",
"userid",
"read",
];

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

@@ -10,15 +10,21 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int $id
* @property string|null $args
* @property string|null $error
* @property string|null $start_at 开始时间
* @property string|null $end_at 结束时间
* @property \Illuminate\Support\Carbon|null $start_at 开始时间
* @property \Illuminate\Support\Carbon|null $end_at 结束时间
* @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
@@ -142,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' => 0,
],
]);
default:

View File

@@ -13,34 +13,40 @@ use Carbon\Carbon;
* App\Models\User
*
* @property int $userid
* @property array $identity 身份
* @property array $department 所属部门
* @property array $identity
* @property array $department
* @property string|null $az A-Z
* @property string|null $pinyin 拼音(主要用于搜索)
* @property string|null $email 邮箱
* @property string|null $email
* @property string|null $tel 联系电话
* @property string $nickname 昵称
* @property string|null $profession 职位/职称
* @property string $userimg 头像
* @property string $nickname
* @property string|null $profession
* @property string $userimg
* @property string|null $encrypt
* @property string|null $password 登录密码
* @property int|null $changepass 登录需要修改密码
* @property int|null $login_num 累计登录次数
* @property string|null $last_ip 最后登录IP
* @property string|null $last_at 最后登录时间
* @property \Illuminate\Support\Carbon|null $last_at 最后登录时间
* @property string|null $line_ip 最后在线IP接口
* @property string|null $line_at 最后在线时间(接口)
* @property \Illuminate\Support\Carbon|null $line_at 最后在线时间(接口)
* @property int|null $task_dialog_id 最后打开的任务会话ID
* @property string|null $created_ip 注册IP
* @property string|null $disable_at 禁用时间(离职时间)
* @property \Illuminate\Support\Carbon|null $disable_at
* @property int|null $email_verity 邮箱是否已验证
* @property int|null $bot 是否机器人
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \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)
@@ -89,7 +95,13 @@ class User extends AbstractModel
*/
public function getNicknameAttribute($value)
{
return $value ?: Base::cardFormat($this->email);
if ($value) {
if (UserBot::isSystemBot($this->email)) {
return Doo::translate($value);
}
return $value;
}
return Base::formatName($this->email);
}
/**
@@ -144,7 +156,7 @@ class User extends AbstractModel
});
$array = [];
foreach ($list as $item) {
$array[] = $item['name'] . ($item['owner_userid'] === $this->userid ? '(M)' : '');
$array[] = $item['name'] . ($item['owner_userid'] === $this->userid ? ' (M)' : '');
}
return implode(', ', $array);
}
@@ -527,6 +539,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
@@ -561,6 +583,8 @@ class User extends AbstractModel
return url("images/avatar/default_claude.png");
case 'ai-gemini@bot.system':
return url("images/avatar/default_gemini.png");
case 'ai-zhipu@bot.system':
return url("images/avatar/default_zhipu.png");
case 'bot-manager@bot.system':
return url("images/avatar/default_bot.png");
case 'meeting-alert@bot.system':
@@ -633,7 +657,9 @@ class User extends AbstractModel
])->save();
}
//
$update['nickname'] = UserBot::systemBotName($email);
if (empty($update['nickname'])) {
$update['nickname'] = UserBot::systemBotName($email);
}
}
if ($update) {
$botUser->updateInstance($update);

View File

@@ -16,14 +16,20 @@ use Carbon\Carbon;
* @property int|null $userid 所属人ID
* @property int|null $bot_id 机器人ID
* @property int|null $clear_day 消息自动清理天数
* @property string|null $clear_at 下一次清理时间
* @property \Illuminate\Support\Carbon|null $clear_at 下一次清理时间
* @property string|null $webhook_url 消息webhook地址
* @property int|null $webhook_num 消息webhook请求次数
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|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)
@@ -38,6 +44,16 @@ use Carbon\Carbon;
class UserBot extends AbstractModel
{
/**
* 判断是否系统机器人
* @param $email
* @return bool
*/
public static function isSystemBot($email)
{
return str_ends_with($email, '@bot.system') && self::systemBotName($email);
}
/**
* 系统机器人名称
* @param $name string 邮箱 或 邮箱前缀
@@ -48,7 +64,7 @@ class UserBot extends AbstractModel
if (str_contains($name, "@")) {
$name = explode("@", $name)[0];
}
return match ($name) {
$name = match ($name) {
'system-msg' => '系统消息',
'task-alert' => '任务提醒',
'check-in' => '签到打卡',
@@ -59,11 +75,13 @@ class UserBot extends AbstractModel
'ai-wenxin' => '文心一言',
'ai-qianwen' => '通义千问',
'ai-gemini' => 'Gemini',
'ai-zhipu' => '智谱清言',
'bot-manager' => '机器人管理',
'meeting-alert' => '会议通知',
'okr-alert' => 'OKR提醒',
default => '', // 不是系统机器人时返回空(也可以拿来判断是否是系统机器人)
};
return Doo::translate($name);
}
/**
@@ -78,7 +96,7 @@ class UserBot extends AbstractModel
[
'key' => 'checkin',
'label' => Doo::translate('我要打卡')
], [
], /*[
'key' => 'it',
'label' => Doo::translate('IT资讯')
], [
@@ -93,7 +111,7 @@ class UserBot extends AbstractModel
], [
'key' => 'soup',
'label' => Doo::translate('心灵鸡汤')
]
]*/
],
'anon-msg@bot.system' => [
[
@@ -110,7 +128,7 @@ class UserBot extends AbstractModel
'label' => Doo::translate('帮助指令')
], [
'key' => '/api',
'label' => Doo::translate('Api接口文档')
'label' => Doo::translate('API接口文档')
], [
'key' => '/list',
'label' => Doo::translate('我的机器人')
@@ -120,6 +138,7 @@ class UserBot extends AbstractModel
'ai-claude@bot.system',
'ai-wenxin@bot.system',
'ai-gemini@bot.system',
'ai-zhipu@bot.system',
'ai-qianwen@bot.system' => [
[
'key' => '%3A.clear',
@@ -212,7 +231,20 @@ class UserBot extends AbstractModel
];
$checkins[] = [
'userid' => $UserInfo->userid,
'remark' => '手动签到',
'remark' => $setting['manual_remark'] ?: 'Manual',
];
}
} elseif (Base::leftExists($mac, "checkin-", true)) {
$mac = Base::leftDelete($mac, "checkin-", true);
if ($UserInfo = User::whereUserid($mac)->whereBot(0)->first()) {
$array = [
'userid' => $UserInfo->userid,
'mac' => '00:00:00:00:00:00',
'date' => $nowDate,
];
$checkins[] = [
'userid' => $UserInfo->userid,
'remark' => $setting['face_remark'] ?: 'Machine',
];
}
}
@@ -242,11 +274,15 @@ class UserBot extends AbstractModel
};
$sendMsg = function($type, $checkin) use ($alreadyTip, $getJokeSoup, $botUser, $nowDate) {
$cacheKey = "Checkin::sendMsg-{$nowDate}-{$type}:" . $checkin['userid'];
$typeDesc = $type == "up" ? "上班" : "下班";
$typeContent = $type == "up" ? "上班" : "下班";
if (Cache::get($cacheKey) === "yes") {
if ($alreadyTip && $dialog = WebSocketDialog::checkUserDialog($botUser, $checkin['userid'])) {
$text = "<p>今日已{$typeDesc}打卡,无需重复打卡。</p>";
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, $type != "up");
$text = "今日已{$typeContent}打卡,无需重复打卡。";
$text .= $checkin['remark'] ? " ({$checkin['remark']})": "";
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'content' => $text,
], $botUser->userid, false, false, $type != "up");
}
return;
}
@@ -255,12 +291,21 @@ class UserBot extends AbstractModel
if ($dialog = WebSocketDialog::checkUserDialog($botUser, $checkin['userid'])) {
$hi = date("H:i");
$remark = $checkin['remark'] ? " ({$checkin['remark']})": "";
$text = "<p>{$typeDesc}打卡成功,打卡时间: {$hi}{$remark}</p>";
$suff = $getJokeSoup($type);
if ($suff) {
$text = "{$text}<p>----------</p><p>{$suff}</p>";
}
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, $type != "up");
$subcontent = $getJokeSoup($type);
$title = "{$typeContent}打卡成功,打卡时间: {$hi}{$remark}";
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'title' => $title,
'content' => [
[
'content' => $title
], [
'content' => $subcontent,
'language' => false,
'style' => 'padding-top:4px;opacity:0.6',
]
],
], $botUser->userid, false, false, $type != "up");
}
};
if ($timeAdvance <= Base::time() && Base::time() < $timeEnd) {
@@ -282,14 +327,20 @@ class UserBot extends AbstractModel
/**
* 隐私机器人
* @param $command
* @return string
* @return array
*/
public static function anonBotQuickMsg($command)
{
return match ($command) {
"help" => "使用说明:打开你想要发匿名消息的个人对话,点击输入框右边的 ⊕ 号,选择 <u>匿名消息</u> 即可输入你想要发送的匿名消息内容。",
"privacy" => "匿名消息将通过 <u>匿名消息(机器人)</u> 发送给对方,不会记录你的身份信息。",
default => '',
"help" => [
"title" => "匿名消息使用说明",
"content" => "使用说明:打开你想要发匿名消息的个人对话,点击输入框右边的 ⊕ 号,选择「匿名消息」即可输入你想要发送的匿名消息内容。"
],
"privacy" => [
"title" => "匿名消息隐私说明",
"content" => "匿名消息将通过「匿名消息(机器人)」发送给对方,不会记录你的身份信息。"
],
default => [],
};
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace App\Models;
use App\Exceptions\ApiException;
use App\Module\Base;
use App\Module\Ihttp;
/**
* App\Models\UserCheckinFace
*
* @property int $id
* @property int|null $userid 会员id
* @property string|null $faceimg 人脸图片
* @property int|null $status 状态
* @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|UserCheckinFace newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereFaceimg($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereRemark($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereUserid($value)
* @mixin \Eloquent
*/
class UserCheckinFace extends AbstractModel
{
public static function saveFace($userid, $nickname, $faceimg, $remark='')
{
// 取上传图片的URL
$faceimg = Base::unFillUrl($faceimg);
$record = "";
if ($faceimg != '') {
$faceFile = public_path($faceimg);
$record = base64_encode(file_get_contents($faceFile));
}
$url = 'http://' . env('APP_IPPR') . '.14' . ":7788/user";
$data = [
'name' => $nickname,
'enrollid' => $userid,
'admin' => 0,
'backupnum' => 50,
];
if ($record != '') {
$data['record'] = $record;
}
$res = Ihttp::ihttp_post($url, json_encode($data), 15);
if($res['data'] && $data = json_decode($res['data'])){
if($data->ret != 1 && $data->msg){
throw new ApiException($data->msg);
}
}
return AbstractModel::transaction(function() use ($userid, $faceimg, $remark) {
$checkinFace = self::query()->whereUserid($userid)->first();
if ($checkinFace) {
self::updateData(['id' => $checkinFace->id], [
'faceimg' => $faceimg,
'status' => 1,
'remark' => $remark
]);
} else {
$checkinFace = new UserCheckinFace();
$checkinFace->faceimg = $faceimg;
$checkinFace->userid = $userid;
$checkinFace->remark = $remark;
$checkinFace->save();
}
if ($faceimg == '') {
$res = UserCheckinFace::deleteDeviceUser($userid);
if ($res) {
return $res;
}
}
return Base::retSuccess('设置成功');
});
}
public static function deleteDeviceUser($userid) {
$url = 'http://' . env('APP_IPPR') . '.14' . ":7788/user/delete";
$data = [
'enrollid' => $userid,
'backupnum' => 50, // 13 删除整个用户 50 删除图片
];
$res = Ihttp::ihttp_post($url, json_encode($data));
if($res['data'] && $data = json_decode($res['data'])){
if($data->ret != 1 && $data->msg){
throw new ApiException($data->msg);
// return Base::retError($data->msg);
}
}
}
}

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)
@@ -42,7 +48,7 @@ class UserDelete extends AbstractModel
$value = Base::json2array($value);
// 昵称
if (!$value['nickname']) {
$value['nickname'] = Base::cardFormat($value['email']);
$value['nickname'] = Base::formatName($value['email']);
}
// 头像
$value['userimg'] = User::getAvatar($value['userid'], $value['userimg'], $value['email'], $value['nickname']);
@@ -65,7 +71,7 @@ class UserDelete extends AbstractModel
}
$cache = $row->cache;
$cache = array_intersect_key($cache, array_flip(array_merge(User::$basicField, ['department_name'])));
$cache['delete_at'] = $row->created_at->format($row->dateFormat ?: 'Y-m-d H:i:s');
$cache['delete_at'] = $row->created_at->toDateTimeString();
return $cache;
}
}

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)
@@ -87,7 +93,7 @@ class UserEmailVerification extends AbstractModel
->send();
} catch (\Throwable $e) {
if (str_contains($e->getMessage(), "Timed Out")) {
throw new ApiException("language.TimedOut");
throw new ApiException("邮件发送超时,请检查邮箱配置是否正确");
} elseif ($e->getCode() === 550) {
throw new ApiException('邮件内容被拒绝,请检查邮箱是否开启接收功能');
} else {

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)
@@ -47,6 +53,7 @@ class UserTransfer extends AbstractModel
WebSocketDialog::select(['web_socket_dialogs.*'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->where('web_socket_dialogs.type', 'group')
->where('web_socket_dialogs.group_type', '!=', 'okr')
->where('u.userid', $this->original_userid)
->orderByDesc('web_socket_dialogs.id')
->chunk(100, function($list) {

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.hide', '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.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.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;
}
@@ -149,13 +194,14 @@ 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 ? Carbon::parse($this->last_msg->created_at)->format('Y-m-d H:i:s') : null;
$this->last_msg = $this->last_msg ?? WebSocketDialogMsg::whereDialogId($this->id)->find($this->search_msg_id);
$this->last_at = $this->last_msg ? Carbon::parse($this->last_msg->created_at)->toDateTimeString() : null;
} else {
// 未读信息
if (Base::judgeClientVersion("0.34.0")) {
@@ -168,11 +214,11 @@ class WebSocketDialog extends AbstractModel
// 是否免打扰
$this->silence = $this->silence ?? $dialogUserFun('silence');
// 对话人数
$this->people = WebSocketDialogUser::whereDialogId($this->id)->count();
$this->people = $this->people ?? WebSocketDialogUser::whereDialogId($this->id)->count();
// 有待办
$this->todo_num = WebSocketDialogMsgTodo::whereDialogId($this->id)->whereUserid($userid)->whereDoneAt(null)->count();
$this->todo_num = $this->todo_num ?? WebSocketDialogMsgTodo::whereDialogId($this->id)->whereUserid($userid)->whereDoneAt(null)->count();
// 最后消息
$this->last_msg = WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first();
$this->last_msg = $this->last_msg ?? WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first();
}
// 对方信息
$this->pinyin = Base::cn2pinyin($this->name);
@@ -552,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
@@ -622,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) {
@@ -630,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();
}
}
@@ -695,6 +727,7 @@ class WebSocketDialog extends AbstractModel
{
$filePath = '';
$result = [];
$data = [];
foreach ($dialogIds as $dialog_id) {
$dialog = WebSocketDialog::checkDialog($dialog_id);
//
@@ -705,18 +738,19 @@ class WebSocketDialog extends AbstractModel
"image64" => $image64,
"path" => $path,
"fileName" => $fileName,
"quality" => 85
]);
} else if ($filePath) {
Base::makeDir(public_path($path));
copy($filePath, public_path($path) . basename($filePath));
} else {
$setting = Base::setting('system');
$data = Base::upload([
"file" => $files,
"type" => 'more',
"path" => $path,
"fileName" => $fileName,
"size" => ($setting['file_upload_limit'] ?: 0) * 1024
"quality" => 100,
"convertVideo" => true
]);
}
//

View File

@@ -4,6 +4,7 @@ namespace App\Models;
use Carbon\Carbon;
use App\Module\Base;
use App\Module\Doo;
use App\Module\Image;
use App\Tasks\PushTask;
use App\Exceptions\ApiException;
@@ -33,18 +34,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 +56,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 +81,6 @@ class WebSocketDialogMsg extends AbstractModel
protected $appends = [
'percentage',
'reply_data',
'forward_data',
];
protected $hidden = [
@@ -107,36 +108,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 +118,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 +138,34 @@ 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']);
$textUserid = is_array($msg['text_userid']) ? $msg['text_userid'] : [];
if (isset($msg['text_userid'])) {
unset($msg['text_userid']);
}
if ($msg['text'] && !in_array(Doo::userId(), $textUserid)) {
$msg['text'] = "";
}
}
return $msg;
}
/**
* 获取占比
* @param bool|int $increment 是否新增阅读数
@@ -205,6 +200,7 @@ class WebSocketDialogMsg extends AbstractModel
'msg_id' => $this->id,
'userid' => $userid,
'after' => 1,
'live' => 1,
]);
if ($msgRead->saveOrIgnore()) {
$this->send = WebSocketDialogMsgRead::whereMsgId($this->id)->count();
@@ -333,41 +329,54 @@ class WebSocketDialogMsg extends AbstractModel
if (in_array($this->type, ['tag', 'todo', 'notice'])) {
return Base::retError('此消息不支持设待办');
}
if ($this->todo && $this->todo != $sender) {
return Base::retError('仅支持设此待办人员【' . User::userid2nickname($this->todo) . '】取消');
}
$before = $this->todo;
$this->todo = $before ? 0 : $sender;
$current = WebSocketDialogMsgTodo::whereMsgId($this->id)->pluck('userid')->toArray();
$cancel = array_diff($current, $userids);
$setup = array_diff($userids, $current);
//
$this->todo = $setup || count($current) > count($cancel) ? $sender : 0;
$this->save();
$resData = [
$upData = [
'id' => $this->id,
'todo' => $this->todo,
'dialog_id' => $this->dialog_id,
];
$dialog = WebSocketDialog::find($this->dialog_id);
$dialog->pushMsg('update', $upData);
//
$data = [
'update' => $resData
$retData = [
'add' => [],
'update' => $upData
];
$res = self::sendMsg(null, $this->dialog_id, 'todo', [
'action' => $this->todo ? 'add' : 'remove',
'data' => [
'id' => $this->id,
'type' => $this->type,
'msg' => $this->quoteTextMsg(),
'userids' => implode(",", $userids),
]
], $sender);
if (Base::isSuccess($res)) {
$data['add'] = $res['data'];
$dialog = WebSocketDialog::find($this->dialog_id);
$dialog->pushMsg('update', array_merge($resData, ['dialog_id' => $this->dialog_id]));
//
if ($this->todo) {
if ($cancel) {
$res = self::sendMsg(null, $this->dialog_id, 'todo', [
'action' => 'remove',
'data' => [
'id' => $this->id,
'type' => $this->type,
'msg' => $this->quoteTextMsg(),
'userids' => implode(",", $cancel),
]
], $sender);
if (Base::isSuccess($res)) {
$retData['add'][] = $res['data'];
WebSocketDialogMsgTodo::whereMsgId($this->id)->whereIn('userid', $cancel)->delete();
}
}
if ($setup) {
$res = self::sendMsg(null, $this->dialog_id, 'todo', [
'action' => 'add',
'data' => [
'id' => $this->id,
'type' => $this->type,
'msg' => $this->quoteTextMsg(),
'userids' => implode(",", $setup),
]
], $sender);
if (Base::isSuccess($res)) {
$retData['add'][] = $res['data'];
$useridList = $dialog->dialogUser->pluck('userid')->toArray();
foreach ($useridList as $userid) {
if ($userids && !in_array($userid, $userids)) {
continue;
}
if (empty($userid)) {
foreach ($setup as $userid) {
if (!in_array($userid, $useridList)) {
continue;
}
WebSocketDialogMsgTodo::createInstance([
@@ -376,30 +385,40 @@ class WebSocketDialogMsg extends AbstractModel
'userid' => $userid,
])->saveOrIgnore();
}
} else {
WebSocketDialogMsgTodo::whereMsgId($this->id)->delete();
}
} else {
$this->todo = $before;
$this->save();
}
//
return Base::retSuccess($this->todo ? '设置成功' : '取消成功', $data);
return Base::retSuccess($this->todo ? '设置成功' : '取消成功', $retData);
}
/**
* 转发消息
* @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;
if ($forwardData['show'] === 0) {
// 如果上一条消息不显示原发送者信息,则转发的消息原始数据为当前消息
$forwardId = $this->id;
$forwardUserid = $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,13 +426,16 @@ 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;
}
if ($leaveMessage) {
self::sendMsg(null, $dialogid, 'text', ['text' => $leaveMessage], $user->userid);
$res = self::sendMsg(null, $dialogid, 'text', ['text' => $leaveMessage], $user->userid);
if (Base::isSuccess($res)) {
$msgs[] = $res['data'];
}
}
}
}
@@ -427,16 +449,22 @@ 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'];
}
if ($leaveMessage) {
self::sendMsg(null, $dialog->id, 'text', ['text' => $leaveMessage], $user->userid);
$res = self::sendMsg(null, $dialog->id, 'text', ['text' => $leaveMessage], $user->userid);
if (Base::isSuccess($res)) {
$msgs[] = $res['data'];
}
}
}
}
}
if (count($msgs) > 0) {
$this->increment('forward_num', count($msgs));
}
return Base::retSuccess('转发成功', [
'msgs' => $msgs
]);
@@ -458,9 +486,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 +528,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
],
]
@@ -528,45 +555,127 @@ class WebSocketDialogMsg extends AbstractModel
}
switch ($data['type']) {
case 'text':
case 'word-chain':
case 'vote':
return $this->previewTextMsg($data['msg']['text'], $preserveHtml);
case 'vote':
$action = Doo::translate("投票");
return "[{$action}] {$this->previewTextMsg($data['msg']['text'], $preserveHtml)}";
case 'word-chain':
$action = Doo::translate("接龙");
return "[{$action}] {$this->previewTextMsg($data['msg']['text'], $preserveHtml)}";
case 'record':
return "[语音]";
$action = Doo::translate("语音");
return "[{$action}]";
case 'meeting':
return "[会议] ${$data['msg']['name']}";
$action = Doo::translate("会议");
return "[{$action}] ${$data['msg']['name']}";
case 'file':
if ($data['msg']['type'] == 'img') {
return "[图片]";
}
return "[文件] {$data['msg']['name']}";
return $this->previewFileMsg($data['msg']);
case 'tag':
$action = $data['msg']['action'] === 'remove' ? '取消标注' : '标注';
$action = Doo::translate($data['msg']['action'] === 'remove' ? '取消标注' : '标注');
return "[{$action}] {$this->previewMsg(false, $data['msg']['data'])}";
case 'top':
$action = $data['msg']['action'] === 'remove' ? '取消置顶' : '置顶';
$action = Doo::translate($data['msg']['action'] === 'remove' ? '取消置顶' : '置顶');
return "[{$action}] {$this->previewMsg(false, $data['msg']['data'])}";
case 'todo':
$action = $data['msg']['action'] === 'remove' ? '取消待办' : ($data['msg']['action'] === 'done' ? '完成' : '设待办');
$action = Doo::translate($data['msg']['action'] === 'remove' ? '取消待办' : ($data['msg']['action'] === 'done' ? '完成' : '设待办'));
return "[{$action}] {$this->previewMsg(false, $data['msg']['data'])}";
case 'notice':
return $data['msg']['notice'];
case 'template':
return $this->previewTemplateMsg($data['msg']);
default:
return "[未知的消息]";
$action = Doo::translate("未知的消息");
return "[{$action}]";
}
}
/**
* 生成关键词
* 预览文件消息
* @param $msg
* @return string
*/
public function generateMsgKey()
private function previewFileMsg($msg)
{
return match ($this->type) {
'text' => str_replace("&nbsp;", " ", strip_tags($this->msg['text'])),
'meeting', 'file' => $this->msg['name'],
default => '',
};
if ($msg['type'] == 'img') {
$action = Doo::translate("图片");
return "[{$action}]";
} elseif ($msg['ext'] == 'mp4') {
$action = Doo::translate("视频");
return "[{$action}]";
}
$action = Doo::translate("文件");
return "[{$action}] {$msg['name']}";
}
/**
* 预览模板消息
* @param $msg
* @return string
*/
private function previewTemplateMsg($msg)
{
if (!empty($msg['title_raw'])) {
return $msg['title_raw'];
}
if (!empty($msg['title'])) {
return Doo::translate($msg['title']);
}
if ($msg['type'] === 'content' && is_string($msg['content']) && $msg['content'] !== '') {
return Doo::translate($msg['content']);
}
return Doo::translate('未知的消息');
}
/**
* 生成关键词并保存
* @param string $key
* @return void
*/
public function generateKeyAndSave(string $key = ''): void
{
if (empty($key)) {
$key = '';
switch ($this->type) {
case 'text':
if (!preg_match("/<span[^>]*?data-quick-key=([\"'])(.*?)\\1[^>]*?>/is", $this->msg['text'])) {
$key = strip_tags($this->msg['text']);
}
break;
case 'vote':
case 'word-chain':
$key = strip_tags($this->msg['text']);
break;
case 'file':
$key = $this->msg['name'];
$key = preg_replace("/^(image|\d+)\.(png|jpg|jpeg|webp|gif)$/i", "", $key);
$key = preg_replace("/^LongText-(.*?)/i", "", $key);
break;
case 'meeting':
$key = $this->msg['name'];
break;
}
}
$key = str_replace(["&quot;", "&amp;", "&lt;", "&gt;"], "", $key);
$key = str_replace(["\r", "\n", "\t", "&nbsp;"], " ", $key);
$key = preg_replace("/^\/[A-Za-z]+/", " ", $key);
$key = preg_replace("/\s+/", " ", $key);
$this->key = trim($key);
$this->save();
}
/**
@@ -622,9 +731,9 @@ class WebSocketDialogMsg extends AbstractModel
$imagePath = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/";
Base::makeDir(public_path($imagePath));
$imagePath .= md5s($base64) . "." . $matchs[1][$key];
if (Base::saveContentImage(public_path($imagePath), base64_decode($base64))) {
if (Base::saveContentImage(public_path($imagePath), base64_decode($base64), 90)) {
$imageSize = getimagesize(public_path($imagePath));
if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0)) {
if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0, 80)) {
$imagePath .= "_thumb.{$extension}";
}
$text = str_replace($matchs[0][$key], "[:IMAGE:browse:{$imageSize[0]}:{$imageSize[1]}:{$imagePath}::]", $text);
@@ -698,7 +807,7 @@ class WebSocketDialogMsg extends AbstractModel
}
if (file_exists(public_path($imagePath))) {
$imageSize = getimagesize(public_path($imagePath));
if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0)) {
if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0, 80)) {
$imagePath .= "_thumb.{$extension}";
}
$text = str_replace($matchs[0][$key], "[:IMAGE:browse:{$imageSize[0]}:{$imageSize[1]}:{$imagePath}::]", $text);
@@ -706,9 +815,9 @@ class WebSocketDialogMsg extends AbstractModel
$image = file_get_contents($str);
if (empty($image)) {
$text = str_replace($matchs[0][$key], "[:IMAGE:browse:90:90:images/other/imgerr.jpg::]", $text);
} else if (Base::saveContentImage(public_path($imagePath), $image)) {
} else if (Base::saveContentImage(public_path($imagePath), $image, 90)) {
$imageSize = getimagesize(public_path($imagePath));
if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0)) {
if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0, 80)) {
$imagePath .= "_thumb.{$extension}";
}
$text = str_replace($matchs[0][$key], "[:IMAGE:browse:{$imageSize[0]}:{$imageSize[1]}:{$imagePath}::]", $text);
@@ -752,6 +861,15 @@ class WebSocketDialogMsg extends AbstractModel
$text = str_replace($str, "[:QUICK:{$quickKey}:{$quickLabel}:]", $text);
}
}
// 处理 li 标签
preg_match_all("/<li[^>]*?>/i", $text, $matchs);
foreach ($matchs[0] as $str) {
if (preg_match("/data-list=['\"](bullet|ordered|checked|unchecked)['\"]/i", $str, $match)) {
$text = str_replace($str, '<li data-list="' . $match[1] . '">', $text);
} else {
$text = str_replace($str, '<li>', $text);
}
}
// 处理链接标签
preg_match_all("/<a[^>]*?href=([\"'])(.*?)\\1[^>]*?>(.*?)<\/a>/is", $text, $matchs);
foreach ($matchs[0] as $key => $str) {
@@ -786,7 +904,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|em|p|s|u)(.*?)\>/is", function (array $match) { // 不用去除 li 和 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);
@@ -817,9 +948,10 @@ class WebSocketDialogMsg extends AbstractModel
* @param bool $push_retry 推送-失败后重试1次有时候在事务里执行数据还没生成时会出现找不到消息的情况
* @param bool|null $push_silence 推送-静默
* - type = [text|file|record|meeting] 默认为false
* @param string|null $search_key 搜索关键词(用于搜索,留空则自动生成)
* @return array
*/
public static function sendMsg($action, $dialog_id, $type, $msg, $sender = null, $push_self = false, $push_retry = false, $push_silence = null)
public static function sendMsg($action, $dialog_id, $type, $msg, $sender = null, $push_self = false, $push_retry = false, $push_silence = null, $search_key = null)
{
$link = 0;
$mtype = $type;
@@ -846,10 +978,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,22 +1003,30 @@ 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 (!in_array($dialogMsg->type, ['text', 'template'])) {
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();
$dialogMsg->generateKeyAndSave($search_key);
//
WebSocketDialogUser::whereDialogId($dialog->id)->whereUserid($sender)->whereHide(1)->change([
'hide' => 0, // 修改消息时,显示会话(仅自己)
@@ -902,36 +1042,50 @@ 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 ($search_key, $dialogMsg) {
$dialogMsg->send = 1;
$dialogMsg->key = $dialogMsg->generateMsgKey();
$dialogMsg->save();
$dialogMsg->generateKeyAndSave($search_key);
//
WebSocketDialogUser::whereDialogId($dialog->id)->change([
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'),
]);
});

View File

@@ -15,15 +15,25 @@ use Carbon\Carbon;
* @property int|null $silence 是否免打扰0否1是
* @property int|null $email 是否发了邮件
* @property int|null $after 在阅读之后才添加的记录
* @property string|null $read_at 阅读时间
* @property int|null $dot 红点标记
* @property int|null $live 是否在会话里
* @property \Illuminate\Support\Carbon|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 whereDot($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereLive($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereMention($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereMsgId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereReadAt($value)

View File

@@ -9,11 +9,17 @@ namespace App\Models;
* @property int|null $dialog_id 对话ID
* @property int|null $msg_id 消息ID
* @property int|null $userid 接收会员ID
* @property string|null $done_at 完成时间
* @property \Illuminate\Support\Carbon|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

@@ -10,24 +10,34 @@ use Carbon\Carbon;
* @property int $id
* @property int|null $dialog_id 对话ID
* @property int|null $userid 会员ID
* @property string|null $top_at 置顶时间
* @property \Illuminate\Support\Carbon|null $top_at 置顶时间
* @property \Illuminate\Support\Carbon|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)
@@ -37,8 +47,6 @@ use Carbon\Carbon;
*/
class WebSocketDialogUser extends AbstractModel
{
protected $dateFormat = 'Y-m-d H:i:s.v';
/**
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
@@ -46,4 +54,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

@@ -6,6 +6,7 @@ use App\Exceptions\ApiException;
use App\Models\Setting;
use App\Models\Tmp;
use Cache;
use Carbon\Carbon;
use Overtrue\Pinyin\Pinyin;
use Redirect;
use Request;
@@ -127,7 +128,7 @@ class Base
public static function checkClientVersion($min)
{
if (!self::judgeClientVersion($min)) {
throw new ApiException('当前版本 (v' . Base::getClientVersion() . ') 过低,最低版本要求 (v' . $min . ')。');
throw new ApiException('当前客户端版本 (' . Base::getClientVersion() . ') 过低,最低版本要求 (' . $min . ')。');
}
}
@@ -359,17 +360,6 @@ class Base
}
}
/**
* 去除html
* @param $text
* @param int $length
* @return string
*/
public static function getHtml($text, $length = 250)
{
return Base::cutStr(strip_tags($text), $length, 0, "...");
}
/**
*
* 截取字符串
@@ -1263,6 +1253,38 @@ class Base
return $string;
}
/**
* 替换开头指定字符串
* @param $string
* @param $find
* @param $replace
* @param $lower
* @return mixed|string
*/
public static function leftReplace($string, $find, $replace, $lower = false)
{
if (Base::leftExists($string, $find, $lower)) {
$string = $replace . substr($string, strlen($find));
}
return $string;
}
/**
* 替换结尾指定字符串
* @param $string
* @param $find
* @param $replace
* @param $lower
* @return mixed|string
*/
public static function rightReplace($string, $find, $replace, $lower = false)
{
if (Base::rightExists($string, $find, $lower)) {
$string = substr($string, 0, strlen($find) * -1) . $replace;
}
return $string;
}
/**
* 截取指定字符串
* @param $str
@@ -1640,7 +1662,7 @@ class Base
public static function forumDate($date)
{
$dur = time() - $date;
if ($date > strtotime(date("Y-m-d"))) {
if ($date > Carbon::now()->startOf('day')->timestamp) {
//今天
if ($dur < 60) {
return max($dur, 1) . '秒前';
@@ -1651,10 +1673,10 @@ class Base
} else {
return date("H:i", $date);
}
} elseif ($date > strtotime(date("Y-m-d", strtotime("-1 day")))) {
} elseif ($date > Carbon::now()->subDays()->startOf('day')->timestamp) {
//昨天
return '昨天';
} elseif ($date > strtotime(date("Y-m-d", strtotime("-2 day")))) {
} elseif ($date > Carbon::now()->subDays(2)->startOf('day')->timestamp) {
//前天
return '前天';
} elseif ($dur > 86400) {
@@ -1665,23 +1687,22 @@ class Base
}
/**
* 获取时间戳今天的第一秒时间戳
* @param $time
* @return false|int
* 创建Carbon对象
* @param $var
* @return Carbon
*/
public static function dayTimeF($time)
public static function newCarbon($var)
{
return strtotime(date("Y-m-d 00:00:00", self::isNumber($time) ? $time : strtotime($time)));
}
/**
* 获取时间戳今天的最后一秒时间戳
* @param $time
* @return false|int
*/
public static function dayTimeE($time)
{
return strtotime(date("Y-m-d 23:59:59", self::isNumber($time) ? $time : strtotime($time)));
if (self::isNumber($var)) {
if (preg_match("/^\d{13,}$/", $var)) {
$var = $var / 1000;
}
return Carbon::createFromTimestamp($var);
} elseif (is_string($var)) {
return Carbon::parse(trim($var));
} else {
return Carbon::now();
}
}
/**
@@ -1689,9 +1710,9 @@ class Base
* @param $str
* @return string
*/
public static function cardFormat($str)
public static function formatName($str)
{
if (strpos($str, '@')) {
if (str_contains($str, "@")) {
$email_array = explode("@", $str);
$prevfix = substr($str, 0, strlen($email_array[0]) < 4 ? 1 : 3); //邮箱前缀
$count = 0;
@@ -2105,7 +2126,7 @@ class Base
/**
* image64图片保存
* @param array $param [ image64=带前缀的base64, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式], autoThumb=>false不要自动生成缩略图, 'compress'=>是否压缩图片(默认true) ]
* @param array $param [ image64=带前缀的base64, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式], autoThumb=>false不要自动生成缩略图, 'quality'=>压缩图片质量(默认0不压缩) ]
* @return array [name=>文件名, size=>文件大小(单位KB),file=>绝对地址, path=>相对地址, url=>全路径地址, ext=>文件后缀名]
*/
public static function image64save($param)
@@ -2148,19 +2169,19 @@ class Base
"height" => -1, //图片高度
"ext" => $extension, //文件后缀名
];
//图片尺寸
// 图片尺寸
$paramet = getimagesize($array['file']);
$array['width'] = $paramet[0];
$array['height'] = $paramet[1];
//原图压缩
// 原图裁剪
if ($param['scale'] && is_array($param['scale'])) {
list($width, $height) = $param['scale'];
if (($width > 0 && $array['width'] > $width) || ($height > 0 && $array['height'] > $height)) {
$cut = ($width > 0 && $height > 0) ? 'cover' : 'percentage';
$cut = $param['scale'][2] ?? $cut;
//图片压缩
// 图片裁剪
$tmpFile = $array['file'] . '_tmp.jpg';
if (Image::thumbImage($array['file'], $tmpFile, $width, $height, $cut)) {
if (Image::thumbImage($array['file'], $tmpFile, $width, $height, 90, $cut)) {
$tmpSize = filesize($tmpFile);
if ($tmpSize > $fileSize) {
@unlink($tmpFile);
@@ -2169,11 +2190,11 @@ class Base
rename($tmpFile, $array['file']);
}
}
//图片尺寸
// 更新图片尺寸
$paramet = getimagesize($array['file']);
$array['width'] = $paramet[0];
$array['height'] = $paramet[1];
//重命名
// 重命名
if ($scaleName) {
$scaleName = str_replace(['{WIDTH}', '{HEIGHT}'], [$array['width'], $array['height']], $scaleName);
if (rename($array['file'], Base::rightDelete($array['file'], $fileName) . $scaleName)) {
@@ -2185,8 +2206,9 @@ class Base
}
}
// 压缩图片
if ($param['compress'] !== false) {
Image::compressImage($array['file']);
$quality = intval($param['quality']);
if ($quality > 0) {
Image::compressImage($array['file'], null, $quality);
$array['size'] = Base::twoFloat(filesize($array['file']) / 1024, true);
}
//生成缩略图
@@ -2195,7 +2217,7 @@ class Base
$param['autoThumb'] = false;
}
if ($param['autoThumb'] !== false) {
if ($extension = Image::thumbImage($array['file'], $array['file'] . "_thumb.{*}", 320, 0)) {
if ($extension = Image::thumbImage($array['file'], $array['file'] . "_thumb.{*}", 320, 0, 80)) {
$array['thumb'] .= "_thumb.{$extension}";
}
}
@@ -2208,8 +2230,26 @@ class Base
/**
* 上传文件
* @param array $param [ type=[文件类型], file=>Request::file, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式], size=>限制大小KB, autoThumb=>false不要自动生成缩略图, chmod=>权限(默认0644), 'compress'=>是否压缩图片(默认true) ]
* @return array [name=>原文件名, size=>文件大小(单位KB),file=>绝对地址, path=>相对地址, url=>全路径地址, ext=>文件后缀名]
* @param array $param [
type=[文件类型],
file=>Request::file,
path=>文件路径,
fileName=>文件名称,
scale=>[压缩原图宽,高, 压缩方式],
size=>限制大小KB,
autoThumb=>false不要自动生成缩略图,
chmod=>权限(默认0644),
quality=>压缩图片质量(默认0不压缩),
convertVideo=>转换视频格式(默认false) ,
]
* @return array [
name=>原文件名,
size=>文件大小(单位KB),
file=>绝对地址,
path=>相对地址,
url=>全路径地址,
ext=>文件后缀名,
]
*/
public static function upload($param)
{
@@ -2229,7 +2269,7 @@ class Base
$type = ['jpg', 'jpeg', 'webp', 'gif', 'png'];
break;
case 'video':
$type = ['rm', 'rmvb', 'wmv', 'avi', 'mpg', 'mpeg', 'mp4'];
$type = ['rm', 'rmvb', 'wmv', 'avi', 'mpg', 'mpeg', 'mp4', 'mov', 'webm'];
break;
case 'audio':
$type = ['mp3', 'wma', 'wav', 'amr'];
@@ -2244,7 +2284,7 @@ class Base
$type = ['zip'];
break;
case 'file':
$type = ['jpg', 'jpeg', 'webp', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz', 'ai', 'avi', 'bmp', 'cdr', 'eps', 'mov', 'mp3', 'mp4', 'pr', 'psd', 'svg', 'tif'];
$type = ['jpg', 'jpeg', 'webp', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz', 'ai', 'avi', 'bmp', 'cdr', 'eps', 'mp3', 'mp4', 'mov', 'webm', 'pr', 'psd', 'svg', 'tif'];
break;
case 'firmware':
$type = ['img', 'tar', 'bin'];
@@ -2252,8 +2292,8 @@ class Base
case 'md':
$type = ['md'];
break;
case 'desktop':
$type = ['yml', 'yaml', 'dmg', 'pkg', 'blockmap', 'zip', 'exe', 'msi'];
case 'publish':
$type = ['yml', 'yaml', 'dmg', 'pkg', 'blockmap', 'zip', 'exe', 'msi', 'apk'];
break;
case 'more':
$type = []; // 不限制上传文件类型
@@ -2265,10 +2305,15 @@ class Base
if ($type && !in_array($extension, $type)) {
return Base::retError('文件格式错误,限制类型:' . implode(",", $type));
}
$limitSize = intval($param['size']);
if ($limitSize <= 0) {
$fileUploadLimit = intval(Base::settingFind('system', 'file_upload_limit', 0));
$limitSize = $fileUploadLimit * 1024;
}
try {
$fileSize = $file->getSize();
if ($param['size'] > 0 && $fileSize > $param['size'] * 1024) {
return Base::retError('文件大小超限,最大限制:' . $param['size'] . 'KB');
if ($limitSize > 0 && $fileSize > $limitSize * 1024) {
return Base::retError('文件大小超限,最大限制:' . $limitSize . 'KB');
}
} catch (\Throwable) {
$fileSize = 0;
@@ -2314,8 +2359,8 @@ class Base
return Base::retError('上传失败');
}
@chmod($array['file'], $chmod);
//iOS照片颠倒处理
if (in_array($extension, ['jpg', 'jpeg']) && function_exists('exif_read_data')) {
// iOS照片颠倒处理
if (in_array($array['ext'], ['jpg', 'jpeg']) && function_exists('exif_read_data')) {
$data = imagecreatefromstring(file_get_contents($array['file']));
$exif = @exif_read_data($array['file']);
if (!empty($exif['Orientation'])) {
@@ -2331,20 +2376,51 @@ class Base
}
}
//
if (in_array($extension, ['jpg', 'jpeg', 'webp', 'gif', 'png'])) {
//图片尺寸
if ($param['convertVideo'] && in_array($array['ext'], ['mov', 'webm'])) {
// 转换视频格式
$output = Base::rightReplace($array['file'], ".{$array['ext']}", '.mp4');
if ($array['ext'] === 'webm') {
shell_exec("ffmpeg -y -i {$array['file']} -strict experimental {$output} 2>&1");
} else {
shell_exec("ffmpeg -y -i {$array['file']} -c:v copy -c:a copy {$output} 2>&1");
}
if (file_exists($output) && filesize($output) > 0) {
@unlink($array['file']);
$array = array_merge($array, [
"name" => Base::rightReplace($array['name'], ".{$array['ext']}", '.mp4'),
"size" => Base::twoFloat(filesize($output) / 1024, true),
"file" => $output,
"path" => Base::rightReplace($array['path'], ".{$array['ext']}", '.mp4'),
"url" => Base::rightReplace($array['url'], ".{$array['ext']}", '.mp4'),
"ext" => 'mp4',
]);
}
}
if (in_array($array['ext'], ['mov', 'webm', 'mp4'])) {
// 视频尺寸
$thumbFile = $array['file'] . '_thumb.jpg';
shell_exec("ffmpeg -y -i {$array['file']} -ss 1 -vframes 1 {$thumbFile} 2>&1");
if (file_exists($thumbFile) && filesize($thumbFile) > 0) {
$paramet = getimagesize($thumbFile);
$array['width'] = $paramet[0];
$array['height'] = $paramet[1];
$array['thumb'] = $array['path'] . '_thumb.jpg';
}
}
if (in_array($array['ext'], ['jpg', 'jpeg', 'webp', 'gif', 'png'])) {
// 获取图片尺寸
$paramet = getimagesize($array['file']);
$array['width'] = $paramet[0];
$array['height'] = $paramet[1];
//原图压缩
// 原图裁剪
if ($param['scale'] && is_array($param['scale'])) {
list($width, $height) = $param['scale'];
if (($width > 0 && $array['width'] > $width) || ($height > 0 && $array['height'] > $height)) {
$cut = ($width > 0 && $height > 0) ? 'cover' : 'percentage';
$cut = $param['scale'][2] ?? $cut;
//图片压缩
// 图片裁剪
$tmpFile = $array['file'] . '_tmp.jpg';
if (Image::thumbImage($array['file'], $tmpFile, $width, $height, $cut)) {
if (Image::thumbImage($array['file'], $tmpFile, $width, $height, 90, $cut)) {
$tmpSize = filesize($tmpFile);
if ($tmpSize > $fileSize) {
@unlink($tmpFile);
@@ -2353,11 +2429,11 @@ class Base
rename($tmpFile, $array['file']);
}
}
//图片尺寸
// 更新图片尺寸
$paramet = getimagesize($array['file']);
$array['width'] = $paramet[0];
$array['height'] = $paramet[1];
//重命名
// 重命名
if ($scaleName) {
$scaleName = str_replace(['{WIDTH}', '{HEIGHT}'], [$array['width'], $array['height']], $scaleName);
if (rename($array['file'], Base::rightDelete($array['file'], $fileName) . $scaleName)) {
@@ -2368,21 +2444,22 @@ class Base
}
}
}
//生成缩略图
// 生成缩略图
$array['thumb'] = $array['path'];
if ($extension === 'gif' && !isset($param['autoThumb'])) {
if ($array['ext'] === 'gif' && !isset($param['autoThumb'])) {
$param['autoThumb'] = false;
}
if ($param['autoThumb'] !== false) {
if ($extension = Image::thumbImage($array['file'], $array['file'] . "_thumb.{*}", 320, 0)) {
$array['thumb'] .= "_thumb.{$extension}";
if ($array['ext'] = Image::thumbImage($array['file'], $array['file'] . "_thumb.{*}", 320, 0, 80)) {
$array['thumb'] .= "_thumb.{$array['ext']}";
}
}
$array['thumb'] = Base::fillUrl($array['thumb']);
}
// 压缩图片
if ($param['compress'] !== false) {
Image::compressImage($array['file']);
$quality = intval($param['quality']);
if ($quality > 0) {
Image::compressImage($array['file'], null, $quality);
$array['size'] = Base::twoFloat(filesize($array['file']) / 1024, true);
}
//
@@ -2430,7 +2507,8 @@ class Base
*/
public static function isThumb($file): bool
{
return str_ends_with($file, '_thumb.jpg')
return str_ends_with($file, '_thumb.jpeg')
|| str_ends_with($file, '_thumb.jpg')
|| str_ends_with($file, '_thumb.png');
}
@@ -2441,7 +2519,9 @@ class Base
*/
public static function getThumbExt($file): string
{
if (file_exists($file . '_thumb.jpg')) {
if (file_exists($file . '_thumb.jpeg')) {
return 'jpeg';
} elseif (file_exists($file . '_thumb.jpg')) {
return 'jpg';
} elseif (file_exists($file . '_thumb.png')) {
return 'png';
@@ -2457,7 +2537,9 @@ class Base
*/
public static function thumbRestore($file): mixed
{
if (str_ends_with($file, '_thumb.jpg')) {
if (str_ends_with($file, '_thumb.jpeg')) {
return Base::rightDelete($file, '_thumb.jpeg');
} elseif (str_ends_with($file, '_thumb.jpg')) {
return Base::rightDelete($file, '_thumb.jpg');
} elseif (str_ends_with($file, '_thumb.png')) {
return Base::rightDelete($file, '_thumb.png');
@@ -2556,18 +2638,6 @@ class Base
return array_values($arr[$key]);
}
/**
* 获取当前是本月第几个星期
* @return float
*/
public static function getMonthWeek()
{
$time = strtotime(date("Y-m-01"));
$w = date('w', $time);
$j = date("j");
return ceil(($j . $w) / 7);
}
/**
* 把返回的数据集转换成Tree
* @param array $list 要转换的数据集
@@ -2909,17 +2979,10 @@ class Base
*/
public static function streamDownload($file, $name = null)
{
$contentType = 'application/octet-stream';
if ($name && !str_contains($name, '.')) {
$name .= ".";
}
//
if ($file instanceof \Closure) {
return Response::streamDownload($file, $name, [
'Content-Type' => $contentType,
]);
}
//
if (!$file instanceof File) {
if ($file instanceof \SplFileInfo) {
$file = new File($file->getPathname());
@@ -2930,12 +2993,23 @@ class Base
if (!$file->isReadable()) {
throw new FileException('File must be readable.');
}
$contentType = $file->getMimeType() ?: $contentType;
$content = $file->getContent();
return Response::streamDownload(function() use ($content) {
echo $content;
}, $name, [
'Content-Type' => $contentType,
// 大于100M直接下载
if ($file->getSize() > 100 * 1024 * 1024) {
return Response::download($file->getPathname(), $name);
}
//
$filePath = $file->getPathname();
return Response::stream(function () use ($filePath) {
$fileStream = fopen($filePath, 'r');
while (!feof($fileStream)) {
echo fread($fileStream, 1024);
flush();
}
fclose($fileStream);
}, 200, [
'Content-Type' => 'application/octet-stream',
'Content-Disposition' => 'attachment; filename="'.$name.'"',
'Content-Length' => $file->getSize(),
]);
}
@@ -2943,13 +3017,13 @@ class Base
* 保存图片到文件(同时压缩)
* @param $path
* @param $content
* @param $compress
* @param int $quality 压缩图片质量(默认0不压缩)
* @return bool
*/
public static function saveContentImage($path, $content, $compress = true) {
public static function saveContentImage($path, $content, int $quality = 0) {
if (file_put_contents($path, $content)) {
if ($compress) {
Image::compressImage($path);
if ($quality > 0) {
Image::compressImage($path, null, $quality);
}
return true;
}

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

@@ -12,6 +12,49 @@ use Illuminate\Support\Facades\Config;
*/
class Extranet
{
/**
* 通过 openAI 语音转文字
* @param string $filePath
* @return array
*/
public static function openAItranscriptions($filePath)
{
if (!file_exists($filePath)) {
return Base::retError("语音文件不存在");
}
$systemSetting = Base::setting('system');
$aibotSetting = Base::setting('aibotSetting');
if ($systemSetting['voice2text'] !== 'open' || empty($aibotSetting['openai_key'])) {
return Base::retError("语音转文字功能未开启");
}
//
$extra = [
'Content-Type' => 'multipart/form-data',
'Authorization' => 'Bearer ' . $aibotSetting['openai_key'],
];
if ($aibotSetting['openai_agency']) {
$extra['CURLOPT_PROXY'] = $aibotSetting['openai_agency'];
if (str_contains($aibotSetting['openai_agency'], 'socks')) {
$extra['CURLOPT_PROXYTYPE'] = CURLPROXY_SOCKS5;
} else {
$extra['CURLOPT_PROXYTYPE'] = CURLPROXY_HTTP;
}
}
$res = Ihttp::ihttp_request('https://api.openai.com/v1/audio/transcriptions', [
'file' => new \CURLFile($filePath),
'model' => 'whisper-1'
], $extra, 15);
if (Base::isError($res)) {
return Base::retError("语音转文字失败", $res);
}
$resData = Base::json2array($res['data']);
if (empty($resData['text'])) {
return Base::retError("语音转文字失败", $resData);
}
//
return Base::retSuccess("success", $resData['text']);
}
/**
* 获取IP地址经纬度
* @param string $ip
@@ -287,18 +330,66 @@ class Extranet
return $text;
}
/**
* 获取搜狗表情包
* @param $keyword
* @return array
*/
public static function sticker($keyword)
{
$data = self::curl("https://pic.sogou.com/napi/wap/searchlist", 1800, 15, [], [
'CURLOPT_CUSTOMREQUEST' => 'POST',
'CURLOPT_POSTFIELDS' => json_encode([
"initQuery" => $keyword . " 表情",
"queryFrom" => "wap",
"ie" => "utf8",
"keyword" => $keyword . " 表情",
// "mode" => 20,
"showMode" => 0,
"start" => 1,
"reqType" => "client",
"reqFrom" => "wap_result",
"prevIsRedis" => "n",
"pagetype" => 0,
"amsParams" => []
]),
'CURLOPT_HTTPHEADER' => [
'Content-Type: application/json',
'Referer: https://pic.sogou.com/'
]
]);
$data = Base::json2array($data);
if ($data['status'] === 0 && $data['data']['picResult']['items']) {
$data = $data['data']['picResult']['items'];
$data = array_filter($data, function ($item) {
return intval($item['thumbHeight']) > 10 && intval($item['thumbWidth']) > 10;
});
return array_map(function ($item) {
return [
'name' => $item['title'],
'src' => $item['thumbUrl'],
'height' => $item['thumbHeight'],
'width' => $item['thumbWidth'],
];
}, $data);
}
return [];
}
/**
* @param $url
* @param int $cacheSecond 缓存时间如果结果为空则缓存有效30秒
* @param int $timeout
* @param array $post
* @param array $extra
* @return string
*/
private static function curl($url, int $cacheSecond = 0, int $timeout = 15): string
private static function curl($url, int $cacheSecond = 0, int $timeout = 15, array $post = [], array $extra = []): string
{
if ($cacheSecond > 0) {
$key = "curlCache::" . md5($url);
$content = Cache::remember($key, Carbon::now()->addSeconds($cacheSecond), function () use ($cacheSecond, $key, $timeout, $url) {
$result = Ihttp::ihttp_request($url, [], [], $timeout);
$key = "curlCache::" . md5($url) . "::" . md5(json_encode($post)) . "::" . md5(json_encode($extra));
$content = Cache::remember($key, Carbon::now()->addSeconds($cacheSecond), function () use ($extra, $post, $cacheSecond, $key, $timeout, $url) {
$result = Ihttp::ihttp_request($url, $post, $extra, $timeout);
$content = Base::isSuccess($result) ? trim($result['data']) : '';
if (empty($content) && $cacheSecond > 30) {
Cache::put($key, "", Carbon::now()->addSeconds(30));
@@ -306,7 +397,7 @@ class Extranet
return $content;
});
} else {
$result = Ihttp::ihttp_request($url, [], [], $timeout);
$result = Ihttp::ihttp_request($url, $post, $extra, $timeout);
$content = Base::isSuccess($result) ? trim($result['data']) : '';
}
//

View File

@@ -35,8 +35,12 @@ class Ihttp
if($post) {
if (is_array($post)) {
$filepost = false;
foreach ($post as $name => $value) {
if (is_string($value) && substr($value, 0, 1) == '@') {
foreach ($post as $value) {
if (is_string($value) && str_starts_with($value, '@')) {
$filepost = true;
break;
}
if ($value instanceof \CURLFile) {
$filepost = true;
break;
}

View File

@@ -171,10 +171,11 @@ class Image
* @param string $savePath 保存路径
* @param int $width 宽度
* @param int $height 高度
* @param int $quality 压缩质量0-100, 0 为不压缩
* @param string $mode 模式percentage|cover|contain
* @return string|null 成功返回图片后缀,失败返回 false
*/
public static function thumbImage(string $imagePath, string $savePath, int $width, int $height, string $mode = 'percentage'): ?string
public static function thumbImage(string $imagePath, string $savePath, int $width, int $height, int $quality = 0, string $mode = 'percentage'): ?string
{
if (!file_exists($imagePath)) {
return null;
@@ -187,6 +188,9 @@ class Image
$image = new Image($imagePath);
$image->thumb($width, $height, $mode);
$image->saveTo($savePath);
if ($quality > 0) {
Image::compressImage($savePath, null, $quality);
}
return $extension;
} catch (\ImagickException) {
return null;
@@ -194,14 +198,14 @@ class Image
}
/**
* 压缩图片
* 压缩图片(如果压缩后的图片比原图还大那就直接使用原图)
* @param string $imagePath 图片路径
* @param string|null $savePath 保存路径(默认覆盖原图)
* @param int $quality 压缩质量0-100
* @param float $minSize 最小尺寸单位KB
* @param float $minSize 最小尺寸,小于这个尺寸不压缩单位KB
* @return bool
*/
public static function compressImage(string $imagePath, string $savePath = null, int $quality = 100, float $minSize = 10): bool
public static function compressImage(string $imagePath, string $savePath = null, int $quality = 100, float $minSize = 5): bool
{
if (Base::settingFind("system", "image_compress") === 'close') {
return false;
@@ -209,6 +213,7 @@ class Image
if (!file_exists($imagePath)) {
return false;
}
$quality = min(max($quality, 1), 100);
$imageSize = filesize($imagePath);
if ($minSize > 0 && $imageSize < $minSize * 1024) {
return false;
@@ -217,10 +222,7 @@ class Image
$savePath = $imagePath;
}
$tmpPath = $imagePath . '.compress.tmp';
try {
$image = new Image($imagePath);
$image->compress($quality);
$image->saveTo($tmpPath);
if (self::compressAuto($imagePath, $tmpPath, $quality)) {
if (filesize($tmpPath) >= $imageSize) {
copy($imagePath, $savePath);
unlink($tmpPath);
@@ -228,6 +230,32 @@ class Image
rename($tmpPath, $savePath);
}
return true;
}
return false;
}
/**
* 自动压缩图片仅限于compressImage方法使用
* @param string $imagePath
* @param string $savePath
* @param int $quality
* @return bool
*/
private static function compressAuto(string $imagePath, string $savePath, int $quality = 100): bool
{
if (strtolower(pathinfo($imagePath, PATHINFO_EXTENSION)) === 'png') {
$minQuality = $quality - 20;
$compressedContent = shell_exec("pngquant --quality={$minQuality}-{$quality} --strip - < " . $imagePath);
if ($compressedContent) {
file_put_contents($savePath, $compressedContent);
return true;
}
}
try {
$image = new Image($imagePath);
$image->compress($quality);
$image->saveTo($savePath);
return true;
} catch (\ImagickException) {
return false;
}

View File

@@ -15,22 +15,48 @@ class TimeRange
public function __construct($data)
{
if (is_array($data)) {
$range = $this->format($data['timerange']);
if ($data['updated_at'] || $data['at_after']) {
$range[0] = $data['updated_at'] ?: $data['at_after'];
}
if ($data['deleted_at']) {
$range[1] = $data['deleted_at'];
$keys = array_keys($data);
if (count($keys) === 2 && $keys[0] === 0 && $keys[1] === 1) {
$range = $data;
} else {
$range = $this->format($data['timerange']);
if ($data['updated_at'] || $data['at_after']) {
$range[0] = $data['updated_at'] ?: $data['at_after'];
}
if ($data['deleted_at']) {
$range[1] = $data['deleted_at'];
}
}
} else {
$range = $this->format($data);
}
//
$updated = Base::isNumber($range[0]) ? intval($range[0]) : trim($range[0]);
$deleted = Base::isNumber($range[1]) ? intval($range[1]) : trim($range[1]);
//
$this->updated = $updated ? Carbon::parse($updated) : null;
$this->deleted = $deleted ? Carbon::parse($deleted) : null;
$this->updated = $range[0] ? Base::newCarbon($range[0]) : null;
$this->deleted = $range[1] ? Base::newCarbon($range[1]) : null;
}
/**
* @return Carbon|null
*/
public function firstTime(): ?Carbon
{
return $this->updated;
}
/**
* @return Carbon|null
*/
public function lastTime(): ?Carbon
{
return $this->deleted;
}
/**
* @return bool
*/
public function isExist(): bool
{
return $this->updated && $this->deleted;
}
/**
@@ -42,7 +68,7 @@ class TimeRange
private function format($timerange)
{
$search = str_contains($timerange, ":") ? ["|"] : ["|", "-"];
return explode(",", str_replace($search, ",", $timerange));
return Base::newTrim(explode(",", str_replace($search, ",", $timerange)));
}
/**

View File

@@ -4,6 +4,8 @@ namespace App\Observers;
use App\Models\Deleted;
use App\Models\ProjectTask;
use App\Models\ProjectTaskUser;
use App\Models\ProjectTaskVisibilityUser;
use App\Models\ProjectUser;
class ProjectTaskObserver
@@ -27,11 +29,14 @@ class ProjectTaskObserver
*/
public function updated(ProjectTask $projectTask)
{
if ($projectTask->isDirty('visibility')) {
self::visibilityUpdate($projectTask);
}
if ($projectTask->isDirty('archived_at')) {
if ($projectTask->archived_at) {
Deleted::record('projectTask', $projectTask->id, $this->userids($projectTask));
Deleted::record('projectTask', $projectTask->id, self::userids($projectTask));
} else {
Deleted::forget('projectTask', $projectTask->id, $this->userids($projectTask));
Deleted::forget('projectTask', $projectTask->id, self::userids($projectTask));
}
}
}
@@ -44,7 +49,7 @@ class ProjectTaskObserver
*/
public function deleted(ProjectTask $projectTask)
{
Deleted::record('projectTask', $projectTask->id, $this->userids($projectTask));
Deleted::record('projectTask', $projectTask->id, self::userids($projectTask));
}
/**
@@ -55,7 +60,7 @@ class ProjectTaskObserver
*/
public function restored(ProjectTask $projectTask)
{
Deleted::forget('projectTask', $projectTask->id, $this->userids($projectTask));
Deleted::forget('projectTask', $projectTask->id, self::userids($projectTask));
}
/**
@@ -71,10 +76,50 @@ class ProjectTaskObserver
/**
* @param ProjectTask $projectTask
* @param string[]|string $dataType
* @return array
*/
private function userids(ProjectTask $projectTask)
public static function userids(ProjectTask $projectTask, array|string $dataType = 'project')
{
return ProjectUser::whereProjectId($projectTask->project_id)->pluck('userid')->toArray();
if (!is_array($dataType)) {
$dataType = [$dataType];
}
if (in_array('project', $dataType)) {
return ProjectUser::whereProjectId($projectTask->project_id)->pluck('userid')->toArray();
}
if (in_array('projectOwnerUser', $dataType)) {
return ProjectUser::whereProjectId($projectTask->project_id)->where('owner', 1)->pluck('userid')->toArray();
}
$array = [];
if (in_array('task', $dataType)) {
$array = array_merge($array, ProjectTaskUser::whereTaskId($projectTask->id)->pluck('userid')->toArray());
}
if (in_array('visibility', $dataType)) {
$array = array_merge($array, ProjectTaskVisibilityUser::whereTaskId($projectTask->id)->pluck('userid')->toArray());
}
return array_values(array_filter(array_unique($array)));
}
/**
* 可见性更新
* @param ProjectTask $projectTask
*/
public static function visibilityUpdate(ProjectTask $projectTask)
{
$projectUserids = self::userids($projectTask);
switch ($projectTask->visibility) {
case 1:
Deleted::forget('projectTask', $projectTask->id, $projectUserids);
break;
case 2:
case 3:
$dataType = $projectTask->visibility == 2 ? ['task'] : ['task', 'visibility'];
$forgetUserids = self::userids($projectTask, $dataType);
$projectOwnerUserIds = self::userids($projectTask, 'projectOwnerUser');
$recordUserids = array_diff($projectUserids, $forgetUserids, $projectOwnerUserIds);
Deleted::record('projectTask', $projectTask->id, $recordUserids);
Deleted::forget('projectTask', $projectTask->id, $forgetUserids);
break;
}
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Observers;
use App\Models\Deleted;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsgRead;
use App\Models\WebSocketDialogUser;
class WebSocketDialogObserver
@@ -39,6 +40,7 @@ class WebSocketDialogObserver
public function deleted(WebSocketDialog $webSocketDialog)
{
Deleted::record('dialog', $webSocketDialog->id, $this->userids($webSocketDialog));
WebSocketDialogMsgRead::whereDialogId($webSocketDialog->id)->update(['live' => 0]);
}
/**
@@ -49,7 +51,9 @@ class WebSocketDialogObserver
*/
public function restored(WebSocketDialog $webSocketDialog)
{
Deleted::forget('dialog', $webSocketDialog->id, $this->userids($webSocketDialog));
$userids = $this->userids($webSocketDialog);
Deleted::forget('dialog', $webSocketDialog->id, $userids);
WebSocketDialogMsgRead::whereDialogId($webSocketDialog->id)->whereIn('userid', $userids)->update(['live' => 1]);
}
/**

View File

@@ -3,7 +3,9 @@
namespace App\Observers;
use App\Models\Deleted;
use App\Models\WebSocketDialogMsgRead;
use App\Models\WebSocketDialogUser;
use Carbon\Carbon;
class WebSocketDialogUserObserver
{
@@ -15,7 +17,20 @@ 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);
WebSocketDialogMsgRead::whereDialogId($webSocketDialogUser->dialog_id)->whereUserid($webSocketDialogUser->userid)->update(['live' => 1]);
}
/**
@@ -38,6 +53,7 @@ class WebSocketDialogUserObserver
public function deleted(WebSocketDialogUser $webSocketDialogUser)
{
Deleted::record('dialog', $webSocketDialogUser->dialog_id, $webSocketDialogUser->userid);
WebSocketDialogMsgRead::whereDialogId($webSocketDialogUser->dialog_id)->whereUserid($webSocketDialogUser->userid)->update(['live' => 0]);
}
/**

View File

@@ -62,6 +62,7 @@ class WebSocketService implements WebSocketHandlerInterface
'type' => 'open',
'data' => [
'fd' => $fd,
'ud' => $user->userid,
],
]));
// 通知上线
@@ -193,6 +194,12 @@ class WebSocketService implements WebSocketHandlerInterface
Cache::put("User::fd:" . $fd, "on", Carbon::now()->addDay());
Cache::put("User::online:" . $userid, "on", Carbon::now()->addDay());
//
$all = Base::json2array(Cache::get("User::online:all"));
if (!isset($all[$userid])) {
$all[$userid] = $userid;
Cache::forever("User::online:all", Base::array2json($all));
}
//
WebSocket::updateInsert([
'key' => md5($fd . '@' . $userid)
], [

View File

@@ -93,16 +93,23 @@ class BotReceiveMsgTask extends AbstractTask
}
// 签到机器人
if ($botUser->email === 'check-in@bot.system') {
$text = UserBot::checkinBotQuickMsg($command, $msg->userid);
if ($text) {
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $text], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
$content = UserBot::checkinBotQuickMsg($command, $msg->userid);
if ($content) {
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'template', [
'type' => 'content',
'content' => $content,
], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
}
}
// 隐私机器人
if ($botUser->email === 'anon-msg@bot.system') {
$text = UserBot::anonBotQuickMsg($command);
if ($text) {
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $text], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
$array = UserBot::anonBotQuickMsg($command);
if ($array) {
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'template', [
'type' => 'content',
'title' => $array['title'],
'content' => $array['content'],
], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
}
}
// 管理机器人
@@ -112,15 +119,17 @@ class BotReceiveMsgTask extends AbstractTask
} elseif (UserBot::whereBotId($botUser->userid)->whereUserid($msg->userid)->exists()) {
$isManager = false;
} else {
$text = "非常抱歉,我不是你的机器人,无法完成你的指令。";
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $text], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'template', [
'type' => 'content',
'content' => "非常抱歉,我不是你的机器人,无法完成你的指令。",
], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
return;
}
//
$array = Base::newTrim(explode(" ", "{$command} "));
$type = $array[0];
$data = [];
$notice = "";
$content = "";
if (!$isManager && in_array($type, ['/list', '/newbot'])) {
return; // 这些操作仅支持【机器人管理】机器人
}
@@ -143,20 +152,19 @@ class BotReceiveMsgTask extends AbstractTask
->orderByDesc('id')
->get();
if ($data->isEmpty()) {
$type = "notice";
$notice = "您没有创建机器人。";
$content = "您没有创建机器人。";
}
break;
/**
* 详情
*/
case '/hello':
case '/info':
$botId = $isManager ? $array[1] : $botUser->userid;
$data = $this->botManagerOne($botId, $msg->userid);
if (!$data) {
$type = "notice";
$notice = "机器人不存在。";
$content = "机器人不存在。";
}
break;
@@ -169,27 +177,27 @@ class BotReceiveMsgTask extends AbstractTask
->where('users.bot', 1)
->where('user_bots.userid', $msg->userid)
->count() >= 50) {
$type = "notice";
$notice = "超过最大创建数量。";
$content = "超过最大创建数量。";
break;
}
if (strlen($array[1]) < 2 || strlen($array[1]) > 20) {
$type = "notice";
$notice = "机器人名称由2-20个字符组成。";
$content = "机器人名称由2-20个字符组成。";
break;
}
$data = User::botGetOrCreate("user-" . Base::generatePassword(), [
'nickname' => $array[1]
], $msg->userid);
if (empty($data)) {
$type = "notice";
$notice = "创建失败。";
$content = "创建失败。";
break;
}
$dialog = WebSocketDialog::checkUserDialog($data, $msg->userid);
if ($dialog) {
$text = "<p>您好,我是机器人:{$data->nickname}我的机器人ID是{$data->userid}</p><p>你可以发送 <u><b>/help</b></u> 查看我支持什么命令。</p>";
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $data->userid); // todo 未能在任务end事件来发送任务
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => '/hello',
'title' => '创建成功。',
'data' => $data,
], $data->userid); // todo 未能在任务end事件来发送任务
}
break;
@@ -200,8 +208,7 @@ class BotReceiveMsgTask extends AbstractTask
$botId = $isManager ? $array[1] : $botUser->userid;
$nameString = $isManager ? $array[2] : $array[1];
if (strlen($nameString) < 2 || strlen($nameString) > 20) {
$type = "notice";
$notice = "机器人名称由2-20个字符组成。";
$content = "机器人名称由2-20个字符组成。";
break;
}
$data = $this->botManagerOne($botId, $msg->userid);
@@ -211,8 +218,7 @@ class BotReceiveMsgTask extends AbstractTask
$data->pinyin = Base::cn2pinyin($nameString);
$data->save();
} else {
$type = "notice";
$notice = "机器人不存在。";
$content = "机器人不存在。";
}
break;
@@ -226,8 +232,7 @@ class BotReceiveMsgTask extends AbstractTask
if ($data) {
$data->deleteUser('delete bot');
} else {
$type = "notice";
$notice = "机器人不存在。";
$content = "机器人不存在。";
}
break;
@@ -240,8 +245,7 @@ class BotReceiveMsgTask extends AbstractTask
if ($data) {
User::generateToken($data);
} else {
$type = "notice";
$notice = "机器人不存在。";
$content = "机器人不存在。";
}
break;
@@ -256,8 +260,7 @@ class BotReceiveMsgTask extends AbstractTask
$data->password = Doo::md5s(Base::generatePassword(32), $data->encrypt);
$data->save();
} else {
$type = "notice";
$notice = "机器人不存在。";
$content = "机器人不存在。";
}
break;
@@ -278,8 +281,7 @@ class BotReceiveMsgTask extends AbstractTask
$data->clear_day = $userBot->clear_day;
$data->clear_at = $userBot->clear_at; // 这两个参数只是作为输出,所以不保存
} else {
$type = "notice";
$notice = "机器人不存在。";
$content = "机器人不存在。";
}
break;
@@ -291,8 +293,7 @@ class BotReceiveMsgTask extends AbstractTask
$webhookUrl = $isManager ? $array[2] : $array[1];
$data = $this->botManagerOne($botId, $msg->userid);
if (strlen($webhookUrl) > 255) {
$type = "notice";
$notice = "webhook地址最长仅支持255个字符。";
$content = "webhook地址最长仅支持255个字符。";
} elseif ($data) {
$userBot = UserBot::whereBotId($botId)->whereUserid($msg->userid)->first();
if ($userBot) {
@@ -303,8 +304,7 @@ class BotReceiveMsgTask extends AbstractTask
$data->webhook_url = $userBot->webhook_url ?: '-';
$data->webhook_num = $userBot->webhook_num; // 这两个参数只是作为输出,所以不保存
} else {
$type = "notice";
$notice = "机器人不存在。";
$content = "机器人不存在。";
}
break;
@@ -316,17 +316,16 @@ 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.hide', '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()) {
$type = "notice";
$notice = "没有搜索到相关会话。";
$content = "没有搜索到相关会话。";
} else {
$list->transform(function (WebSocketDialog $item) use ($data) {
return $item->formatData($data->userid);
@@ -334,25 +333,45 @@ class BotReceiveMsgTask extends AbstractTask
$data->list = $list; // 这个参数只是作为输出,所以不保存
}
} else {
$type = "notice";
$notice = "机器人不存在。";
$content = "机器人不存在。";
}
break;
}
//
$text = view('push.bot', [
'type' => $type,
'data' => $data,
'notice' => $notice,
'manager' => $isManager,
'version' => Base::getVersion()
])->render();
if (!$isManager) {
$text = preg_replace("/\s*\{机器人ID\}/", "", $text);
if ($content) {
$msgData = [
'type' => 'content',
'content' => $content,
];
} else {
$msgData = [
'type' => $type,
'data' => $data,
];
$msgData['title'] = match ($type) {
'/hello' => '您好',
'/help' => '帮助指令',
'/list' => '我的机器人',
'/info' => '机器人信息',
'/newbot' => '新建机器人',
'/setname' => '设置名称',
'/deletebot' => '删除机器人',
'/token' => '机器人Token',
'/revoke' => '更新Token',
'/webhook' => '设置Webhook',
'/clearday' => '设置保留消息时间',
'/dialog' => '对话列表',
'/api' => 'API接口文档',
default => '不支持的指令',
};
if ($type == '/api') {
$msgData['version'] = Base::getVersion();
} elseif ($type == '/help') {
$msgData['manager'] = $isManager;
}
}
$text = preg_replace("/^\x20+/", "", $text);
$text = preg_replace("/\n\x20+/", "\n", $text);
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $text], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'template', $msgData, $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
}
}
@@ -369,7 +388,7 @@ class BotReceiveMsgTask extends AbstractTask
$serverUrl = 'http://' . env('APP_IPPR') . '.3';
$userBot = null;
$extras = [];
$error = null;
$errorContent = null;
switch ($botUser->email) {
// ChatGPT 机器人
case 'ai-openai@bot.system':
@@ -383,10 +402,10 @@ class BotReceiveMsgTask extends AbstractTask
'chunk_size' => 7,
];
if (empty($extras['openai_key'])) {
$error = 'Robot disabled.';
$errorContent = '机器人未启用。';
} 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.11).';
$errorContent = '当前客户端版本低(所需版本≥v0.29.11)。';
}
break;
// Claude 机器人
@@ -399,10 +418,10 @@ class BotReceiveMsgTask extends AbstractTask
'server_url' => $serverUrl,
];
if (empty($extras['claude_token'])) {
$error = 'Robot disabled.';
$errorContent = '机器人未启用。';
} 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.11).';
$errorContent = '当前客户端版本低(所需版本≥v0.29.11)。';
}
break;
// Wenxin 机器人
@@ -416,10 +435,10 @@ class BotReceiveMsgTask extends AbstractTask
'server_url' => $serverUrl,
];
if (empty($extras['wenxin_key'])) {
$error = 'Robot disabled.';
$errorContent = '机器人未启用。';
} 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).';
$errorContent = '当前客户端版本低(所需版本≥v0.29.12)。';
}
break;
// QianWen 机器人
@@ -432,10 +451,10 @@ class BotReceiveMsgTask extends AbstractTask
'server_url' => $serverUrl,
];
if (empty($extras['qianwen_key'])) {
$error = 'Robot disabled.';
$errorContent = '机器人未启用。';
} 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).';
$errorContent = '当前客户端版本低(所需版本≥v0.29.12)。';
}
break;
// Gemini 机器人
@@ -450,10 +469,26 @@ class BotReceiveMsgTask extends AbstractTask
'server_url' => $serverUrl,
];
if (empty($extras['gemini_key'])) {
$error = 'Robot disabled.';
$errorContent = '机器人未启用。';
} 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).';
$errorContent = '当前客户端版本低(所需版本≥v0.29.12)。';
}
break;
// 智谱清言 机器人
case 'ai-zhipu@bot.system':
$setting = Base::setting('aibotSetting');
$webhookUrl = "{$serverUrl}/ai/zhipu/send";
$extras = [
'zhipu_key' => $setting['zhipu_key'],
'zhipu_model' => $setting['zhipu_model'],
'server_url' => $serverUrl,
];
if (empty($extras['zhipu_key'])) {
$errorContent = '机器人未启用。';
} elseif (in_array($this->client['platform'], ['win', 'mac', 'web'])
&& !Base::judgeClientVersion("0.29.11", $this->client['version'])) {
$errorContent = '当前客户端版本低所需版本≥v0.29.12)。';
}
break;
// 其他机器人
@@ -462,8 +497,11 @@ class BotReceiveMsgTask extends AbstractTask
$webhookUrl = $userBot?->webhook_url;
break;
}
if ($error) {
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $error], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
if ($errorContent) {
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'template', [
'type' => 'content',
'content' => $errorContent,
], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
return;
}
if (!preg_match("/^https*:\/\//", $webhookUrl)) {
@@ -488,13 +526,19 @@ class BotReceiveMsgTask extends AbstractTask
$userBot->webhook_num++;
$userBot->save();
}
if($res['data'] && $data = json_decode($res['data'])){
if($data['code'] != 200 && $data['message']){
if ($res['data'] && $data = Base::json2array($res['data'])) {
if ($data['code'] != 200 && $data['message']) {
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $res['data']['message']], $botUser->userid, false, false, true);
}
}
} catch (\Throwable $th) {
//throw $th;
info(Base::array2json([
'bot_userid' => $botUser->userid,
'dialog' => $dialog->id,
'msg' => $msg->id,
'webhook_url' => $webhookUrl,
'error' => $th->getMessage(),
]));
}
}

View File

@@ -7,6 +7,7 @@ use App\Models\UserCheckinRecord;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
use App\Module\Base;
use App\Module\Doo;
use App\Module\Extranet;
use Cache;
use Carbon\Carbon;
@@ -83,11 +84,28 @@ class CheckinRemindTask extends AbstractTask
$dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid);
if ($dialog) {
if ($type === 'exceed') {
$text = "<p><strong style='color:red'>缺卡提醒:</strong>上班时间到了,你还没有打卡哦~</p>";
$title = '缺卡提醒';
$style = 'color:#f55;';
$content = '上班时间到了,你还没有打卡哦~';
} else {
$text = "<p><strong>打卡提醒:</strong>快到上班时间了,别忘了打卡哦~</p>";
$title = '打卡提醒';
$style = '';
$content = '快到上班时间了,别忘了打卡哦~';
}
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid);
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'title' => $title,
'content' => [
[
'content' => $title,
'style' => $style . 'font-weight:bold',
],
[
'content' => $content,
'style' => 'padding-top:4px;opacity:0.6',
],
],
], $botUser->userid);
}
}
});

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

@@ -1,75 +0,0 @@
<?php
namespace App\Tasks;
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
use App\Module\Base;
use App\Module\Ihttp;
/**
* Ihttp任务
* Class IhttpTask
* @package App\Tasks
*/
class IhttpTask extends AbstractTask
{
protected $url;
protected $post;
protected $extra;
protected $apiWebsocket;
protected $apiUserid;
protected $endPush = [];
/**
* IhttpTask constructor.
* @param $url
* @param array $post
* @param array $extra
*/
public function __construct($url, $post = [], $extra = [])
{
parent::__construct(...func_get_args());
$this->url = $url;
$this->post = $post;
$this->extra = $extra;
}
/**
* @param mixed $apiWebsocket
*/
public function setApiWebsocket($apiWebsocket): void
{
$this->apiWebsocket = $apiWebsocket;
}
/**
* @param mixed $apiUserid
*/
public function setApiUserid($apiUserid): void
{
$this->apiUserid = $apiUserid;
}
public function start()
{
$res = Ihttp::ihttp_request($this->url, $this->post, $this->extra);
if ($this->apiWebsocket && $this->apiUserid) {
$data = Base::isSuccess($res) ? Base::json2array($res['data']) : $res;
$this->endPush[] = [
'userid' => $this->apiUserid,
'msg' => [
'type' => 'apiWebsocket',
'apiWebsocket' => $this->apiWebsocket,
'apiSuccess' => Base::isSuccess($res),
'data' => $data,
]
];
}
}
public function end()
{
PushTask::push($this->endPush);
}
}

View File

@@ -6,7 +6,6 @@ use App\Models\User;
use App\Module\Base;
use App\Models\Project;
use App\Models\ProjectTask;
use App\Models\ProjectUser;
use Carbon\Carbon;
use App\Models\WebSocketDialogMsg;
use Illuminate\Support\Facades\Cache;
@@ -47,7 +46,7 @@ class UnclaimedTaskRemindTask extends AbstractTask
Project::whereNull('deleted_at')->whereNull('archived_at')->chunk(100, function ($projects) {
foreach ($projects as $project) {
//
$projectTasks = ProjectTask::select('project_tasks.id', 'project_tasks.name')
$projectTasks = ProjectTask::select(['project_tasks.id', 'project_tasks.name'])
->leftJoin('project_task_users', function ($query) {
$query->on('project_tasks.id', '=', 'project_task_users.task_id');
})
@@ -63,17 +62,15 @@ class UnclaimedTaskRemindTask extends AbstractTask
if (empty($botUser)) {
return;
}
if (!ProjectUser::whereUserid($botUser->userid)->whereProjectId($project->id)->exists()) {
$project->joinProject($botUser->userid);
$project->syncDialogUser();
}
//
$taskHtml = '<span style="line-height: 26px;">任务待领取</span> <br/>';
foreach ($projectTasks as $projectTask) {
$taskHtml .= "<span class=\"mention task\" style=\"line-height: 26px;\" data-id=\"{$projectTask->id}\">#{$projectTask->name}</span> <br/>";
}
WebSocketDialogMsg::sendMsg(null, $project->dialog_id, 'text', [
'text' => $taskHtml
WebSocketDialogMsg::sendMsg(null, $project->dialog_id, 'template', [
'type' => 'task_list',
'title' => '任务待领取',
'list' => $projectTasks->map(function ($item) {
return [
'id' => $item->id,
'name' => $item->name,
];
}),
], $botUser->userid);
}
}

View File

@@ -112,24 +112,29 @@ class WebSocketDialogMsgTask extends AbstractTask
if ($userid == $msg->userid) {
$array[$userid] = [
'userid' => $userid,
'mention' => false,
'mention' => 0,
'silence' => $silence,
'dot' => 0,
'updated' => $updated,
];
} else {
$mention = array_intersect([0, $userid], $mentions) ? 1 : 0;
$silence = $mention ? false : $silence;
$dot = $msg->type === 'record' ? 1 : 0;
WebSocketDialogMsgRead::createInstance([
'dialog_id' => $msg->dialog_id,
'msg_id' => $msg->id,
'userid' => $userid,
'mention' => $mention,
'silence' => $silence,
'dot' => $dot,
'live' => 1,
])->saveOrIgnore();
$array[$userid] = [
'userid' => $userid,
'mention' => $mention,
'silence' => $silence,
'dot' => $dot,
'updated' => $updated,
];
// 机器人收到消处理
@@ -169,6 +174,7 @@ class WebSocketDialogMsgTask extends AbstractTask
'silence' => $item['silence'] ? 1 : 0,
'data' => array_merge($msg->toArray(), [
'mention' => $item['mention'],
'dot' => $item['dot'],
'user_at' => Carbon::parse($item['updated'])->toDateTimeString('millisecond'),
'user_ms' => Carbon::parse($item['updated'])->valueOf(),
]),

97
bin/version.js vendored

File diff suppressed because one or more lines are too long

95
cmd
View File

@@ -227,6 +227,63 @@ run_mysql() {
run_exec mariadb "gunzip < /$inputname | mysql -u$username -p$password $database"
run_exec php "php artisan migrate"
judge "还原数据库"
elif [ "$1" = "open" ]; then
container_name=`docker_name mariadb`
if [ -z "$container_name" ]; then
error "没有找到 mariadb 容器!"
exit 1
fi
mkdir -p ${cur_path}/docker/mysql/tmp
cat > ${cur_path}/docker/mysql/tmp/${container_name}.conf <<EOF
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
stream {
upstream mysql {
server ${container_name}:3306 max_fails=1 fail_timeout=30s;
}
server {
listen 3306;
proxy_pass mysql;
proxy_connect_timeout 5s;
}
}
EOF
default_value="$(env_get DB_PORT_OPEN)"
if [ -n "$default_value" ]; then
read_tip="请输入代理端口 (3300-65500, 默认: ${default_value}): "
else
read_tip="请输入代理端口 (3300-65500): "
fi
read -rp "$read_tip" inputport
inputport=${inputport:-$default_value}
if [ $inputport -lt 3300 ] || [ $inputport -gt 65500 ]; then
error "端口范围不正确!"
exit 1
fi
env_set DB_PORT_OPEN $inputport
run_mysql rm-port
docker run --name ${container_name}-port \
--network dootask-networks-$(env_get APP_ID) \
-p ${inputport}:3306 \
-v ${cur_path}/docker/mysql/tmp/${container_name}.conf:/etc/nginx/nginx.conf \
-d nginx:alpine > /dev/null
judge "开启代理"
elif [ "$1" = "close" ]; then
container_name=`docker_name mariadb`
if [ -z "$container_name" ]; then
error "没有找到 mariadb 容器!"
exit 1
fi
docker stop ${container_name}-port > /dev/null
docker rm ${container_name}-port > /dev/null
judge "关闭代理"
elif [ "$1" = "rm-port" ]; then
docker rm -f $(docker_name mariadb)-port &> /dev/null
fi
}
@@ -379,13 +436,18 @@ if [ $# -gt 0 ]; then
exit 1
fi
chmod -R 775 "${cur_path}/docker/mysql/data"
sleep 3
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
error "数据库安装失败!"
exit 1
fi
# 数据库迁移
remaining=20
while [ ! -f "${cur_path}/docker/mysql/data/$(env_get DB_DATABASE)/$(env_get DB_PREFIX)migrations.ibd" ]; do
((remaining=$remaining-1))
if [ $remaining -lt 0 ]; then
error "数据库安装失败!"
exit 1
fi
sleep 3
run_exec php "php artisan migrate --seed"
done
# 设置初始化密码
res=`run_exec mariadb "sh /etc/mysql/repassword.sh"`
$COMPOSE up -d
@@ -421,6 +483,7 @@ if [ $# -gt 0 ]; then
exit 2
;;
esac
run_mysql rm-port
$COMPOSE down
env_set APP_DEBUG "false"
rm -rf "./docker/mysql/data"
@@ -475,6 +538,13 @@ if [ $# -gt 0 ]; 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" == "npm" ]]; then
shift 1
npm $@
cd electron
npm $@
cd ..
docker run --rm -it -v ${cur_path}/resources/mobile:/work -w /work --entrypoint=/bin/bash node:16 -c "npm $@"
elif [[ "$1" == "doc" ]]; then
shift 1
run_exec php "php app/Http/Controllers/Api/apidoc.php"
@@ -498,7 +568,11 @@ if [ $# -gt 0 ]; then
e="php artisan $@" && run_exec php "$e"
elif [[ "$1" == "php" ]]; then
shift 1
e="php $@" && run_exec php "$e"
if [[ "$1" == "restart" ]] || [[ "$1" == "reboot" ]]; then
restart_php
else
e="php $@" && run_exec php "$e"
fi
elif [[ "$1" == "nginx" ]]; then
shift 1
e="nginx $@" && run_exec nginx "$e"
@@ -511,6 +585,10 @@ if [ $# -gt 0 ]; then
run_mysql backup
elif [[ "$1" == "recovery" ]] || [[ "$1" == "r" ]]; then
run_mysql recovery
elif [[ "$1" == "agent" ]] || [[ "$1" == "open" ]]; then
run_mysql open
elif [[ "$1" == "unagent" ]] || [[ "$1" == "close" ]]; then
run_mysql close
else
e="mysql $@" && run_exec mariadb "$e"
fi
@@ -538,6 +616,9 @@ if [ $# -gt 0 ]; then
$COMPOSE stop "$@"
$COMPOSE start "$@"
else
if [[ "$1" == "down" ]]; then
run_mysql rm-port
fi
$COMPOSE "$@"
fi
else

302
composer.lock generated
View File

@@ -2606,28 +2606,28 @@
},
{
"name": "madnest/madzipper",
"version": "v1.2.1",
"version": "v1.2.2",
"source": {
"type": "git",
"url": "https://github.com/madnest/madzipper.git",
"reference": "40d42f13ecbcb3a9bd8847864cdd2ad3afa4bb5e"
"reference": "cb3cc4c98d4f8434dc32f295876220311d4d4217"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/madnest/madzipper/zipball/40d42f13ecbcb3a9bd8847864cdd2ad3afa4bb5e",
"reference": "40d42f13ecbcb3a9bd8847864cdd2ad3afa4bb5e",
"url": "https://api.github.com/repos/madnest/madzipper/zipball/cb3cc4c98d4f8434dc32f295876220311d4d4217",
"reference": "cb3cc4c98d4f8434dc32f295876220311d4d4217",
"shasum": ""
},
"require": {
"ext-zip": "*",
"illuminate/filesystem": "^8.0",
"illuminate/support": "^8.0",
"php": "^7.3|^8.0"
"php": "^8.0"
},
"require-dev": {
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^6.0",
"orchestra/testbench": "^7.0",
"nunomaduro/collision": "^5.0",
"orchestra/testbench": "^6.0",
"phpunit/phpunit": "^9.5"
},
"type": "library",
@@ -2661,9 +2661,9 @@
"description": "Wannabe successor of Chumper/Zipper package for Laravel",
"support": {
"issues": "https://github.com/madnest/madzipper/issues",
"source": "https://github.com/madnest/madzipper/tree/v1.2.1"
"source": "https://github.com/madnest/madzipper/tree/v1.2.2"
},
"time": "2022-05-16T01:31:07+00:00"
"time": "2024-04-05T15:43:20+00:00"
},
{
"name": "maennchen/zipstream-php",
@@ -2975,16 +2975,16 @@
},
{
"name": "monolog/monolog",
"version": "2.9.2",
"version": "2.9.3",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "437cb3628f4cf6042cc10ae97fc2b8472e48ca1f"
"reference": "a30bfe2e142720dfa990d0a7e573997f5d884215"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/437cb3628f4cf6042cc10ae97fc2b8472e48ca1f",
"reference": "437cb3628f4cf6042cc10ae97fc2b8472e48ca1f",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/a30bfe2e142720dfa990d0a7e573997f5d884215",
"reference": "a30bfe2e142720dfa990d0a7e573997f5d884215",
"shasum": ""
},
"require": {
@@ -3005,8 +3005,8 @@
"mongodb/mongodb": "^1.8",
"php-amqplib/php-amqplib": "~2.4 || ^3",
"phpspec/prophecy": "^1.15",
"phpstan/phpstan": "^0.12.91",
"phpunit/phpunit": "^8.5.14",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^8.5.38 || ^9.6.19",
"predis/predis": "^1.1 || ^2.0",
"rollbar/rollbar": "^1.3 || ^2 || ^3",
"ruflin/elastica": "^7",
@@ -3061,7 +3061,7 @@
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
"source": "https://github.com/Seldaek/monolog/tree/2.9.2"
"source": "https://github.com/Seldaek/monolog/tree/2.9.3"
},
"funding": [
{
@@ -3073,7 +3073,7 @@
"type": "tidelift"
}
],
"time": "2023-10-27T15:25:26+00:00"
"time": "2024-04-12T20:52:51+00:00"
},
{
"name": "myclabs/php-enum",
@@ -3518,25 +3518,25 @@
},
{
"name": "orangehill/iseed",
"version": "v3.0.3",
"version": "v3.0.4",
"source": {
"type": "git",
"url": "https://github.com/orangehill/iseed.git",
"reference": "92b98c8bda6280afa0fac016b1b49caf6f57f18d"
"reference": "403ea734f16da238bda7fcc10b8dc549751fa035"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/orangehill/iseed/zipball/92b98c8bda6280afa0fac016b1b49caf6f57f18d",
"reference": "92b98c8bda6280afa0fac016b1b49caf6f57f18d",
"url": "https://api.github.com/repos/orangehill/iseed/zipball/403ea734f16da238bda7fcc10b8dc549751fa035",
"reference": "403ea734f16da238bda7fcc10b8dc549751fa035",
"shasum": ""
},
"require": {
"illuminate/support": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0",
"illuminate/support": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"php": "^7.2|^8.0.2"
},
"require-dev": {
"illuminate/filesystem": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0",
"laravel/framework": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0",
"illuminate/filesystem": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"laravel/framework": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"mockery/mockery": "^1.0.0",
"phpunit/phpunit": "^8.0"
},
@@ -3575,9 +3575,9 @@
],
"support": {
"issues": "https://github.com/orangehill/iseed/issues",
"source": "https://github.com/orangehill/iseed/tree/v3.0.3"
"source": "https://github.com/orangehill/iseed/tree/v3.0.4"
},
"time": "2023-03-27T06:14:43+00:00"
"time": "2024-03-27T08:01:21+00:00"
},
{
"name": "overtrue/http",
@@ -4365,16 +4365,16 @@
},
{
"name": "psy/psysh",
"version": "v0.12.0",
"version": "v0.12.3",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/psysh.git",
"reference": "750bf031a48fd07c673dbe3f11f72362ea306d0d"
"reference": "b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/750bf031a48fd07c673dbe3f11f72362ea306d0d",
"reference": "750bf031a48fd07c673dbe3f11f72362ea306d0d",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73",
"reference": "b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73",
"shasum": ""
},
"require": {
@@ -4438,9 +4438,9 @@
],
"support": {
"issues": "https://github.com/bobthecow/psysh/issues",
"source": "https://github.com/bobthecow/psysh/tree/v0.12.0"
"source": "https://github.com/bobthecow/psysh/tree/v0.12.3"
},
"time": "2023-12-20T15:28:09+00:00"
"time": "2024-04-02T15:57:53+00:00"
},
{
"name": "ralouphie/getallheaders",
@@ -4578,20 +4578,20 @@
},
{
"name": "ramsey/uuid",
"version": "4.7.5",
"version": "4.7.6",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e"
"reference": "91039bc1faa45ba123c4328958e620d382ec7088"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e",
"reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
"reference": "91039bc1faa45ba123c4328958e620d382ec7088",
"shasum": ""
},
"require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11",
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
"ext-json": "*",
"php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0"
@@ -4654,7 +4654,7 @@
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.7.5"
"source": "https://github.com/ramsey/uuid/tree/4.7.6"
},
"funding": [
{
@@ -4666,7 +4666,7 @@
"type": "tidelift"
}
],
"time": "2023-11-08T05:53:05+00:00"
"time": "2024-04-27T21:32:50+00:00"
},
{
"name": "swiftmailer/swiftmailer",
@@ -4746,16 +4746,16 @@
},
{
"name": "symfony/console",
"version": "v5.4.36",
"version": "v5.4.39",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "39f75d9d73d0c11952fdcecf4877b4d0f62a8f6e"
"reference": "f3e591c48688a0cfa1a3296205926c05e84b22b1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/39f75d9d73d0c11952fdcecf4877b4d0f62a8f6e",
"reference": "39f75d9d73d0c11952fdcecf4877b4d0f62a8f6e",
"url": "https://api.github.com/repos/symfony/console/zipball/f3e591c48688a0cfa1a3296205926c05e84b22b1",
"reference": "f3e591c48688a0cfa1a3296205926c05e84b22b1",
"shasum": ""
},
"require": {
@@ -4825,7 +4825,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v5.4.36"
"source": "https://github.com/symfony/console/tree/v5.4.39"
},
"funding": [
{
@@ -4841,7 +4841,7 @@
"type": "tidelift"
}
],
"time": "2024-02-20T16:33:57+00:00"
"time": "2024-04-18T08:26:06+00:00"
},
{
"name": "symfony/css-selector",
@@ -4977,16 +4977,16 @@
},
{
"name": "symfony/error-handler",
"version": "v5.4.36",
"version": "v5.4.39",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
"reference": "90b1d7799bfc1b3ed5f902e8b334eeb7dba537a1"
"reference": "9e02a6e831d6c2dbc5f96c8ff5314d453ecd53cd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/error-handler/zipball/90b1d7799bfc1b3ed5f902e8b334eeb7dba537a1",
"reference": "90b1d7799bfc1b3ed5f902e8b334eeb7dba537a1",
"url": "https://api.github.com/repos/symfony/error-handler/zipball/9e02a6e831d6c2dbc5f96c8ff5314d453ecd53cd",
"reference": "9e02a6e831d6c2dbc5f96c8ff5314d453ecd53cd",
"shasum": ""
},
"require": {
@@ -5028,7 +5028,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/error-handler/tree/v5.4.36"
"source": "https://github.com/symfony/error-handler/tree/v5.4.39"
},
"funding": [
{
@@ -5044,7 +5044,7 @@
"type": "tidelift"
}
],
"time": "2024-02-22T11:40:53+00:00"
"time": "2024-04-18T08:26:06+00:00"
},
{
"name": "symfony/event-dispatcher",
@@ -5210,16 +5210,16 @@
},
{
"name": "symfony/finder",
"version": "v5.4.35",
"version": "v5.4.39",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "abe6d6f77d9465fed3cd2d029b29d03b56b56435"
"reference": "f6a96e4fcd468a25fede16ee665f50ced856bd0a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/abe6d6f77d9465fed3cd2d029b29d03b56b56435",
"reference": "abe6d6f77d9465fed3cd2d029b29d03b56b56435",
"url": "https://api.github.com/repos/symfony/finder/zipball/f6a96e4fcd468a25fede16ee665f50ced856bd0a",
"reference": "f6a96e4fcd468a25fede16ee665f50ced856bd0a",
"shasum": ""
},
"require": {
@@ -5253,7 +5253,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/finder/tree/v5.4.35"
"source": "https://github.com/symfony/finder/tree/v5.4.39"
},
"funding": [
{
@@ -5269,20 +5269,20 @@
"type": "tidelift"
}
],
"time": "2024-01-23T13:51:25+00:00"
"time": "2024-04-18T08:26:06+00:00"
},
{
"name": "symfony/http-foundation",
"version": "v5.4.35",
"version": "v5.4.39",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "f2ab692a22aef1cd54beb893aa0068bdfb093928"
"reference": "3356c93efc30b0c85a37606bdfef16b813faec0e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/f2ab692a22aef1cd54beb893aa0068bdfb093928",
"reference": "f2ab692a22aef1cd54beb893aa0068bdfb093928",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/3356c93efc30b0c85a37606bdfef16b813faec0e",
"reference": "3356c93efc30b0c85a37606bdfef16b813faec0e",
"shasum": ""
},
"require": {
@@ -5329,7 +5329,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v5.4.35"
"source": "https://github.com/symfony/http-foundation/tree/v5.4.39"
},
"funding": [
{
@@ -5345,20 +5345,20 @@
"type": "tidelift"
}
],
"time": "2024-01-23T13:51:25+00:00"
"time": "2024-04-18T08:26:06+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v5.4.37",
"version": "v5.4.39",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "4ef7ed872564852b3c6c15fecf492975a52cbff3"
"reference": "1d812dc3a2863cc4246aaa636b0d71e0bf68e6b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/4ef7ed872564852b3c6c15fecf492975a52cbff3",
"reference": "4ef7ed872564852b3c6c15fecf492975a52cbff3",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/1d812dc3a2863cc4246aaa636b0d71e0bf68e6b0",
"reference": "1d812dc3a2863cc4246aaa636b0d71e0bf68e6b0",
"shasum": ""
},
"require": {
@@ -5407,6 +5407,7 @@
"symfony/stopwatch": "^4.4|^5.0|^6.0",
"symfony/translation": "^4.4|^5.0|^6.0",
"symfony/translation-contracts": "^1.1|^2|^3",
"symfony/var-dumper": "^4.4.31|^5.4",
"twig/twig": "^2.13|^3.0.4"
},
"suggest": {
@@ -5441,7 +5442,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v5.4.37"
"source": "https://github.com/symfony/http-kernel/tree/v5.4.39"
},
"funding": [
{
@@ -5457,7 +5458,7 @@
"type": "tidelift"
}
],
"time": "2024-03-04T20:55:44+00:00"
"time": "2024-04-29T11:17:46+00:00"
},
{
"name": "symfony/mailer",
@@ -5535,16 +5536,16 @@
},
{
"name": "symfony/mime",
"version": "v5.4.35",
"version": "v5.4.39",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "ee94d9b538f93abbbc1ee4ccff374593117b04a9"
"reference": "a5364f016fd9e090f7b4f250a97ea6925a5ca985"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/ee94d9b538f93abbbc1ee4ccff374593117b04a9",
"reference": "ee94d9b538f93abbbc1ee4ccff374593117b04a9",
"url": "https://api.github.com/repos/symfony/mime/zipball/a5364f016fd9e090f7b4f250a97ea6925a5ca985",
"reference": "a5364f016fd9e090f7b4f250a97ea6925a5ca985",
"shasum": ""
},
"require": {
@@ -5565,6 +5566,7 @@
"egulias/email-validator": "^2.1.10|^3.1|^4",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
"symfony/process": "^5.4|^6.4",
"symfony/property-access": "^4.4|^5.1|^6.0",
"symfony/property-info": "^4.4|^5.1|^6.0",
"symfony/serializer": "^5.4.35|~6.3.12|^6.4.3"
@@ -5599,7 +5601,7 @@
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v5.4.35"
"source": "https://github.com/symfony/mime/tree/v5.4.39"
},
"funding": [
{
@@ -5615,7 +5617,7 @@
"type": "tidelift"
}
],
"time": "2024-01-30T08:00:51+00:00"
"time": "2024-04-18T08:26:06+00:00"
},
{
"name": "symfony/options-resolver",
@@ -6473,16 +6475,16 @@
},
{
"name": "symfony/process",
"version": "v5.4.36",
"version": "v5.4.39",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "4fdf34004f149cc20b2f51d7d119aa500caad975"
"reference": "85a554acd7c28522241faf2e97b9541247a0d3d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/4fdf34004f149cc20b2f51d7d119aa500caad975",
"reference": "4fdf34004f149cc20b2f51d7d119aa500caad975",
"url": "https://api.github.com/repos/symfony/process/zipball/85a554acd7c28522241faf2e97b9541247a0d3d5",
"reference": "85a554acd7c28522241faf2e97b9541247a0d3d5",
"shasum": ""
},
"require": {
@@ -6515,7 +6517,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v5.4.36"
"source": "https://github.com/symfony/process/tree/v5.4.39"
},
"funding": [
{
@@ -6531,20 +6533,20 @@
"type": "tidelift"
}
],
"time": "2024-02-12T15:49:53+00:00"
"time": "2024-04-18T08:26:06+00:00"
},
{
"name": "symfony/routing",
"version": "v5.4.37",
"version": "v5.4.39",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
"reference": "48ae43e443693ddb4e574f7c12f0d17ce287694e"
"reference": "5485974ef20de1150dd195a81e9da4915d45263f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/routing/zipball/48ae43e443693ddb4e574f7c12f0d17ce287694e",
"reference": "48ae43e443693ddb4e574f7c12f0d17ce287694e",
"url": "https://api.github.com/repos/symfony/routing/zipball/5485974ef20de1150dd195a81e9da4915d45263f",
"reference": "5485974ef20de1150dd195a81e9da4915d45263f",
"shasum": ""
},
"require": {
@@ -6605,7 +6607,7 @@
"url"
],
"support": {
"source": "https://github.com/symfony/routing/tree/v5.4.37"
"source": "https://github.com/symfony/routing/tree/v5.4.39"
},
"funding": [
{
@@ -6621,20 +6623,20 @@
"type": "tidelift"
}
],
"time": "2024-02-27T09:52:32+00:00"
"time": "2024-04-18T08:26:06+00:00"
},
{
"name": "symfony/service-contracts",
"version": "v2.5.2",
"version": "v2.5.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c"
"reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/a2329596ddc8fd568900e3fc76cba42489ecc7f3",
"reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3",
"shasum": ""
},
"require": {
@@ -6688,7 +6690,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v2.5.2"
"source": "https://github.com/symfony/service-contracts/tree/v2.5.3"
},
"funding": [
{
@@ -6704,7 +6706,7 @@
"type": "tidelift"
}
],
"time": "2022-05-30T19:17:29+00:00"
"time": "2023-04-21T15:04:16+00:00"
},
{
"name": "symfony/string",
@@ -6966,16 +6968,16 @@
},
{
"name": "symfony/var-dumper",
"version": "v5.4.36",
"version": "v5.4.39",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "2e9c2b11267119d9c90d6b3fdce5e4e9f15e2e90"
"reference": "1987f86ad7f339fe3d3e8e6cf3b7ce4d4b8e547e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/2e9c2b11267119d9c90d6b3fdce5e4e9f15e2e90",
"reference": "2e9c2b11267119d9c90d6b3fdce5e4e9f15e2e90",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/1987f86ad7f339fe3d3e8e6cf3b7ce4d4b8e547e",
"reference": "1987f86ad7f339fe3d3e8e6cf3b7ce4d4b8e547e",
"shasum": ""
},
"require": {
@@ -7035,7 +7037,7 @@
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v5.4.36"
"source": "https://github.com/symfony/var-dumper/tree/v5.4.39"
},
"funding": [
{
@@ -7051,7 +7053,7 @@
"type": "tidelift"
}
],
"time": "2024-02-15T11:19:14+00:00"
"time": "2024-04-18T08:26:06+00:00"
},
{
"name": "tightenco/collect",
@@ -7105,6 +7107,7 @@
"issues": "https://github.com/tighten/collect/issues",
"source": "https://github.com/tighten/collect/tree/v9.52.7"
},
"abandoned": "illuminate/collections",
"time": "2023-04-14T21:51:36+00:00"
},
{
@@ -7526,16 +7529,16 @@
},
{
"name": "composer/class-map-generator",
"version": "1.1.0",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/composer/class-map-generator.git",
"reference": "953cc4ea32e0c31f2185549c7d216d7921f03da9"
"reference": "8286a62d243312ed99b3eee20d5005c961adb311"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/class-map-generator/zipball/953cc4ea32e0c31f2185549c7d216d7921f03da9",
"reference": "953cc4ea32e0c31f2185549c7d216d7921f03da9",
"url": "https://api.github.com/repos/composer/class-map-generator/zipball/8286a62d243312ed99b3eee20d5005c961adb311",
"reference": "8286a62d243312ed99b3eee20d5005c961adb311",
"shasum": ""
},
"require": {
@@ -7579,7 +7582,7 @@
],
"support": {
"issues": "https://github.com/composer/class-map-generator/issues",
"source": "https://github.com/composer/class-map-generator/tree/1.1.0"
"source": "https://github.com/composer/class-map-generator/tree/1.1.1"
},
"funding": [
{
@@ -7595,20 +7598,20 @@
"type": "tidelift"
}
],
"time": "2023-06-30T13:58:57+00:00"
"time": "2024-03-15T12:53:41+00:00"
},
{
"name": "composer/pcre",
"version": "3.1.1",
"version": "3.1.3",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9"
"reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9",
"reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9",
"url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
"reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
"shasum": ""
},
"require": {
@@ -7650,7 +7653,7 @@
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.1.1"
"source": "https://github.com/composer/pcre/tree/3.1.3"
},
"funding": [
{
@@ -7666,7 +7669,7 @@
"type": "tidelift"
}
],
"time": "2023-10-11T07:11:09+00:00"
"time": "2024-03-19T10:26:25+00:00"
},
{
"name": "doctrine/cache",
@@ -7763,16 +7766,16 @@
},
{
"name": "doctrine/dbal",
"version": "3.8.3",
"version": "3.8.4",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c"
"reference": "b05e48a745f722801f55408d0dbd8003b403dbbd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/db922ba9436b7b18a23d1653a0b41ff2369ca41c",
"reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/b05e48a745f722801f55408d0dbd8003b403dbbd",
"reference": "b05e48a745f722801f55408d0dbd8003b403dbbd",
"shasum": ""
},
"require": {
@@ -7856,7 +7859,7 @@
],
"support": {
"issues": "https://github.com/doctrine/dbal/issues",
"source": "https://github.com/doctrine/dbal/tree/3.8.3"
"source": "https://github.com/doctrine/dbal/tree/3.8.4"
},
"funding": [
{
@@ -7872,7 +7875,7 @@
"type": "tidelift"
}
],
"time": "2024-03-03T15:55:06+00:00"
"time": "2024-04-25T07:04:44+00:00"
},
{
"name": "doctrine/deprecations",
@@ -8466,16 +8469,16 @@
},
{
"name": "hhxsv5/laravel-s",
"version": "v3.7.37",
"version": "v3.7.38",
"source": {
"type": "git",
"url": "https://github.com/hhxsv5/laravel-s.git",
"reference": "4855c431000a2f904c91c8533cefa8b36a691830"
"reference": "f5123cc80ee98f34bebcf3e50c5d939b4cd64a39"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hhxsv5/laravel-s/zipball/4855c431000a2f904c91c8533cefa8b36a691830",
"reference": "4855c431000a2f904c91c8533cefa8b36a691830",
"url": "https://api.github.com/repos/hhxsv5/laravel-s/zipball/f5123cc80ee98f34bebcf3e50c5d939b4cd64a39",
"reference": "f5123cc80ee98f34bebcf3e50c5d939b4cd64a39",
"shasum": ""
},
"require": {
@@ -8519,7 +8522,7 @@
"email": "hhxsv5@sina.com"
}
],
"description": "🚀 LaravelS is an out-of-the-box adapter between Swoole and Laravel/Lumen.",
"description": "🚀 LaravelS is an out-of-the-box adapter between Laravel/Lumen and Swoole.",
"homepage": "https://github.com/hhxsv5/laravel-s",
"keywords": [
"LaravelS",
@@ -8558,7 +8561,7 @@
"type": "custom"
}
],
"time": "2023-06-06T07:17:34+00:00"
"time": "2024-03-20T10:40:21+00:00"
},
{
"name": "kitloong/laravel-migrations-generator",
@@ -8698,16 +8701,16 @@
},
{
"name": "mockery/mockery",
"version": "1.6.7",
"version": "1.6.11",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
"reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06"
"reference": "81a161d0b135df89951abd52296adf97deb0723d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06",
"reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06",
"url": "https://api.github.com/repos/mockery/mockery/zipball/81a161d0b135df89951abd52296adf97deb0723d",
"reference": "81a161d0b135df89951abd52296adf97deb0723d",
"shasum": ""
},
"require": {
@@ -8719,8 +8722,8 @@
"phpunit/phpunit": "<8.0"
},
"require-dev": {
"phpunit/phpunit": "^8.5 || ^9.6.10",
"symplify/easy-coding-standard": "^12.0.8"
"phpunit/phpunit": "^8.5 || ^9.6.17",
"symplify/easy-coding-standard": "^12.1.14"
},
"type": "library",
"autoload": {
@@ -8777,7 +8780,7 @@
"security": "https://github.com/mockery/mockery/security/advisories",
"source": "https://github.com/mockery/mockery"
},
"time": "2023-12-10T02:24:34+00:00"
"time": "2024-03-21T18:34:15+00:00"
},
{
"name": "myclabs/deep-copy",
@@ -9156,16 +9159,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.26.0",
"version": "1.29.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "231e3186624c03d7e7c890ec662b81e6b0405227"
"reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/231e3186624c03d7e7c890ec662b81e6b0405227",
"reference": "231e3186624c03d7e7c890ec662b81e6b0405227",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc",
"reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc",
"shasum": ""
},
"require": {
@@ -9197,9 +9200,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.26.0"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.0"
},
"time": "2024-02-23T16:05:55+00:00"
"time": "2024-05-06T12:04:23+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -9522,16 +9525,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.17",
"version": "9.6.19",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "1a156980d78a6666721b7e8e8502fe210b587fcd"
"reference": "a1a54a473501ef4cdeaae4e06891674114d79db8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd",
"reference": "1a156980d78a6666721b7e8e8502fe210b587fcd",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8",
"reference": "a1a54a473501ef4cdeaae4e06891674114d79db8",
"shasum": ""
},
"require": {
@@ -9605,7 +9608,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.17"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19"
},
"funding": [
{
@@ -9621,7 +9624,7 @@
"type": "tidelift"
}
],
"time": "2024-02-23T13:14:51+00:00"
"time": "2024-04-05T04:35:58+00:00"
},
{
"name": "psr/cache",
@@ -10474,16 +10477,16 @@
},
{
"name": "sebastian/resource-operations",
"version": "3.0.3",
"version": "3.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/resource-operations.git",
"reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
"reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
"reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
"reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
"shasum": ""
},
"require": {
@@ -10495,7 +10498,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-main": "3.0-dev"
}
},
"autoload": {
@@ -10516,8 +10519,7 @@
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"support": {
"issues": "https://github.com/sebastianbergmann/resource-operations/issues",
"source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
"source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4"
},
"funding": [
{
@@ -10525,7 +10527,7 @@
"type": "github"
}
],
"time": "2020-09-28T06:45:17+00:00"
"time": "2024-03-14T16:00:52+00:00"
},
{
"name": "sebastian/type",

View File

@@ -2,7 +2,6 @@
use App\Models\Project;
use App\Models\ProjectTask;
use Carbon\Carbon;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

View File

@@ -1,8 +1,6 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class InsertSettingColumnTemplate extends Migration
{

View File

@@ -16,7 +16,7 @@ class FilesUpdateExt extends Migration
*/
public function up()
{
File::whereIn('type', ['mind', 'drawio', 'document'])->where('ext', '')->orderBy('id')->chunk(100, function($files) {
File::whereIn('type', ['mind', 'drawio', 'document'])->where('ext', '')->chunkById(100, function($files) {
/** @var File $file */
foreach ($files as $file) {
$fileContent = FileContent::whereFid($file->id)->orderByDesc('id')->first();

View File

@@ -26,11 +26,7 @@ class AddWebSocketDialogMsgsKey extends Migration
\App\Models\WebSocketDialogMsg::chunkById(100, function ($lists) {
/** @var \App\Models\WebSocketDialogMsg $item */
foreach ($lists as $item) {
$key = $item->generateMsgKey();
if ($key) {
$item->key = $key;
$item->save();
}
$item->generateKeyAndSave();
}
});
}

View File

@@ -1,10 +1,7 @@
<?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

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");
});
}
}

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