Compare commits
659 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9eaa575d1a | ||
|
996ed78a0e | ||
|
1759572b2e | ||
|
0a9f9eea90 | ||
|
223fb540b1 | ||
|
c5f1c95f7b | ||
|
3bbcbca926 | ||
|
63c1deb630 | ||
|
424e2428fe | ||
|
2fdef45156 | ||
|
4cd4550a36 | ||
|
16af625aef | ||
|
8e72794c07 | ||
|
d9cf6d7e1b | ||
|
f4e4252227 | ||
|
7107409b1b | ||
|
6c086fab6f | ||
|
d027d67e08 | ||
|
a7d9e635eb | ||
|
e7196efaea | ||
|
0a2a903c74 | ||
|
8104c26b19 | ||
|
101d5c7eb0 | ||
|
cad253b85f | ||
|
f17009a988 | ||
|
1f76278d2b | ||
|
e032d29c91 | ||
|
31d1b0c994 | ||
|
611c6d415c | ||
|
e3b7ac00fd | ||
|
7c952822db | ||
|
b9e435c0e2 | ||
|
356d40e640 | ||
|
123ffd4e66 | ||
|
c952659620 | ||
|
ea8e1e9c57 | ||
|
478d63893b | ||
|
b5ccba552f | ||
|
8d2ee364ba | ||
|
14006068c8 | ||
|
b4358ffc66 | ||
|
33135b1df1 | ||
|
9a66c38e01 | ||
|
81132ecab0 | ||
|
9d89af37be | ||
|
fbc8a36232 | ||
|
ea028ea1a1 | ||
|
35b1c12bb5 | ||
|
fc89d96635 | ||
|
27129652f2 | ||
|
23ef992a7f | ||
|
04533e17ec | ||
|
7f2fcba542 | ||
|
c115e2f985 | ||
|
88642c2003 | ||
|
2c678b5363 | ||
|
bc3b72fafe | ||
|
df3b8cf09c | ||
|
65393b7809 | ||
|
9782c849ad | ||
|
e7efaed08a | ||
|
337b3e5b5d | ||
|
7b1b7d1372 | ||
|
e5b838a2b3 | ||
|
f9cc2ceb11 | ||
|
4f107c5618 | ||
|
b1c5aaff43 | ||
|
d0a4473e2b | ||
|
1c79361094 | ||
|
f72114c223 | ||
|
dbd59cd958 | ||
|
d65f8a3c82 | ||
|
96587a4e45 | ||
|
76ab47c82e | ||
|
af8344c555 | ||
|
10ff02b8a0 | ||
|
cb6cf1e34b | ||
|
953e924aa2 | ||
|
2e0c262d32 | ||
|
cbf2e6a140 | ||
|
49c5a9f621 | ||
|
fb8f63f305 | ||
|
49ff61ad65 | ||
|
695fb60aa4 | ||
|
da20fafa39 | ||
|
d6a9ecd912 | ||
|
f95d721c9c | ||
|
69d6417985 | ||
|
ab2bbc28c8 | ||
|
b0b39429ed | ||
|
ff14cbc752 | ||
|
dd013aaaa3 | ||
|
119f61ef67 | ||
|
a99588c766 | ||
|
6f35fe9936 | ||
|
3112425e43 | ||
|
1ab3aefaa5 | ||
|
be08732e6b | ||
|
97b58b5f9a | ||
|
e4855875cf | ||
|
7a267cc07b | ||
|
f4f351cf9d | ||
|
970b811ab9 | ||
|
99604dbe35 | ||
|
09b1d89718 | ||
|
9d1a9f3134 | ||
|
ccb889233c | ||
|
939f2cbf97 | ||
|
580e0cb36a | ||
|
4bd8835f23 | ||
|
22f32da0c5 | ||
|
aa8a094383 | ||
|
4a72b7f089 | ||
|
a33e4905cf | ||
|
40bd2f0742 | ||
|
1fd73fe79e | ||
|
fd7d3e06f4 | ||
|
7a4d27da69 | ||
|
afbadf7d81 | ||
|
7b65c64431 | ||
|
4f2c0e94d9 | ||
|
7593d7a3e9 | ||
|
35ddc4a472 | ||
|
3400d1e803 | ||
|
f672428fde | ||
|
4171d993d0 | ||
|
d22310ea31 | ||
|
1521d1e883 | ||
|
0412615f6e | ||
|
2bd666efe7 | ||
|
541e1f760b | ||
|
f176bed436 | ||
|
403545cd9b | ||
|
54e4ed27ae | ||
|
e5ddf5616a | ||
|
967c4f04d9 | ||
|
36d4d445a6 | ||
|
5ed0ae2fa9 | ||
|
503a719609 | ||
|
2dcbba63cb | ||
|
5e86bdfda7 | ||
|
4f74a0440c | ||
|
858709f610 | ||
|
6689d48fad | ||
|
8581a7c308 | ||
|
9878efb198 | ||
|
9855c50367 | ||
|
04c59041e0 | ||
|
7fb1ecc9b0 | ||
|
ecd2cdd28e | ||
|
4b0ad22f8d | ||
|
789558da6c | ||
|
3f96117a57 | ||
|
282d6b5746 | ||
|
86d2bb9e2a | ||
|
ab4fbf0437 | ||
|
ae527a78ef | ||
|
8218868681 | ||
|
251f4ca4ac | ||
|
5d5c8ced24 | ||
|
f0ec9b7826 | ||
|
90b8e785a4 | ||
|
cb11a71ff5 | ||
|
54dd90fc4a | ||
|
4bb080bd58 | ||
|
5115a048a3 | ||
|
259351cdd2 | ||
|
972b8f83bc | ||
|
9f6b1c1e25 | ||
|
6229a103aa | ||
|
84116e531b | ||
|
2c1b944b7c | ||
|
0e66ca148d | ||
|
ce70c1ca3a | ||
|
56b91a59a2 | ||
|
8f1fbcb19e | ||
|
a4799a1bc0 | ||
|
cc24ff22e9 | ||
|
72ca335c4c | ||
|
0ef6476e58 | ||
|
6ba63d1466 | ||
|
f9d28e1b6b | ||
|
63534d3eb5 | ||
|
2ab2cf01db | ||
|
2fc039dd70 | ||
|
230d1a1f86 | ||
|
f7921bf388 | ||
|
feed984ba8 | ||
|
5324861f16 | ||
|
ae80939d2e | ||
|
c117e4b087 | ||
|
96580e2284 | ||
|
bc3932c8b8 | ||
|
f7cd4f34d3 | ||
|
3a76f51707 | ||
|
041ba8f2ed | ||
|
17e5d15b1b | ||
|
a6ba59eac0 | ||
|
e89ff02b59 | ||
|
b28be29dc8 | ||
|
9ad85e01de | ||
|
d49790ba78 | ||
|
598f01de95 | ||
|
992e137339 | ||
|
124666cca6 | ||
|
848359bf7b | ||
|
fb24af1900 | ||
|
3d6df3cc09 | ||
|
ea58ed46f2 | ||
|
32817a4275 | ||
|
95033cd5b7 | ||
|
9999548bc2 | ||
|
3d04bd4444 | ||
|
21e618cca2 | ||
|
02eb386155 | ||
|
35bd038802 | ||
|
1ff59aee56 | ||
|
d64fae1832 | ||
|
ae169810d0 | ||
|
3c7619098a | ||
|
a881bfd63b | ||
|
09d3131d46 | ||
|
53550b8975 | ||
|
569164ed56 | ||
|
f61d79d53e | ||
|
bba28d6b57 | ||
|
469f30044a | ||
|
0993d87799 | ||
|
09fa33236f | ||
|
4e9abd6512 | ||
|
2996c0b38e | ||
|
36e366abe0 | ||
|
f9c6c6c127 | ||
|
34772ef2bf | ||
|
305af935a7 | ||
|
33f3c9acbf | ||
|
9e560c79ae | ||
|
705d7f3da0 | ||
|
08372facd7 | ||
|
886baa427b | ||
|
8ff94bc138 | ||
|
d0438390cc | ||
|
d22266a947 | ||
|
e34fb25759 | ||
|
675955b2e6 | ||
|
4516bce0ee | ||
|
4853fbcec3 | ||
|
72e5f9a83e | ||
|
1a1ddc34a2 | ||
|
40ebfb676c | ||
|
3bb7e958dc | ||
|
19dd16fcf0 | ||
|
f596749645 | ||
|
482813ea88 | ||
|
60e47e85a3 | ||
|
22c06fee5e | ||
|
4a42ce87a2 | ||
|
a98e4dbcd4 | ||
|
7baa37ccd1 | ||
|
417017add9 | ||
|
b5e3cc2503 | ||
|
2e11fe2b58 | ||
|
70f1258bab | ||
|
d91fa33330 | ||
|
8f0b5dc049 | ||
|
7f14a82053 | ||
|
b05db6d458 | ||
|
ea87092d73 | ||
|
f468f7ae27 | ||
|
f12c79d292 | ||
|
6e76514a24 | ||
|
88f3d3821e | ||
|
bed2e20a91 | ||
|
c7700bdfef | ||
|
778f6367f9 | ||
|
6115eea401 | ||
|
ff62ef729a | ||
|
d0a432164d | ||
|
0f8d9e64ef | ||
|
d1f5096a16 | ||
|
003de399dc | ||
|
36989ce5ff | ||
|
fa987b4e30 | ||
|
b47c494240 | ||
|
1ab8a19f8e | ||
|
a31134195b | ||
|
5580a4ee3d | ||
|
870276fa48 | ||
|
608660a101 | ||
|
25cb8015d0 | ||
|
e686e8f58c | ||
|
14b699b5b1 | ||
|
ce0c86c8e5 | ||
|
11d1f26724 | ||
|
a8c0978f0d | ||
|
f643911014 | ||
|
0a0227cca4 | ||
|
8bacc3b6ba | ||
|
d516330a41 | ||
|
2d83faf144 | ||
|
c3fd6bf88f | ||
|
2a646becfd | ||
|
a825657516 | ||
|
b71dbe9832 | ||
|
f06b4040bc | ||
|
563cd4b843 | ||
|
834dc9bec9 | ||
|
e6e58a03a6 | ||
|
d9f4adbe26 | ||
|
1777153411 | ||
|
9b0ca581f1 | ||
|
493cf7d46a | ||
|
004bf36dc1 | ||
|
fcf7fb4b9f | ||
|
130a85a5fc | ||
|
d46eec568c | ||
|
7dc6d0ffb2 | ||
|
6529e45868 | ||
|
3ba6ea9c7e | ||
|
b6ff6f5453 | ||
|
cfa529b742 | ||
|
2eabc76c1a | ||
|
463e67d64c | ||
|
1f96af1024 | ||
|
563aa92958 | ||
|
f09b864e30 | ||
|
7ce15d5a71 | ||
|
cc767b164e | ||
|
230ff75c0b | ||
|
da39739fb5 | ||
|
c33be22057 | ||
|
902f35d21b | ||
|
d8426780d2 | ||
|
a1aaf90d2e | ||
|
53ed4d4072 | ||
|
da75554447 | ||
|
04998d6a60 | ||
|
3d88a28465 | ||
|
2dd8f75d52 | ||
|
7a4bdd0ada | ||
|
0ed0b5ee43 | ||
|
2475ee90ee | ||
|
511c19d5aa | ||
|
c4fed86f1e | ||
|
18904ebbaa | ||
|
da3672e6be | ||
|
52e9836bbf | ||
|
3201d90a53 | ||
|
cd9c1d9660 | ||
|
0f54a1f638 | ||
|
713316f87c | ||
|
88d38ba8d1 | ||
|
164f3275f4 | ||
|
a44d6b8b79 | ||
|
942cf57c36 | ||
|
18bc75242b | ||
|
3b38c8b408 | ||
|
22e718423e | ||
|
0dc0e7226e | ||
|
8069784198 | ||
|
a9e71567fe | ||
|
0fae151731 | ||
|
3bb0b55955 | ||
|
071ca80bae | ||
|
1abd78305f | ||
|
e1ef9a94af | ||
|
9f62023175 | ||
|
c9a6d2f5a8 | ||
|
07fca8b895 | ||
|
1a65c4a579 | ||
|
7a28a8950c | ||
|
89e8b0d8b9 | ||
|
74803df5bd | ||
|
2a8e030fb4 | ||
|
c6ebe994cc | ||
|
be3677cfa8 | ||
|
0c2e56271b | ||
|
b019d40009 | ||
|
361484be95 | ||
|
f0ce6cc28f | ||
|
ede298a142 | ||
|
aca1a4d34c | ||
|
e63c5c074c | ||
|
0d0b5dd552 | ||
|
08704e7f60 | ||
|
d48ed18102 | ||
|
3602acd187 | ||
|
ad477eb608 | ||
|
0a393845e9 | ||
|
59b98209ac | ||
|
ac26713f86 | ||
|
9250ef6f65 | ||
|
cabbeb07d0 | ||
|
43171645c0 | ||
|
79c1484288 | ||
|
e730875db3 | ||
|
18e9fb99b5 | ||
|
fd82b9b555 | ||
|
5c3bf0067e | ||
|
ac6b90c986 | ||
|
7dcd8b29fc | ||
|
d951da2c02 | ||
|
605aee35c4 | ||
|
dc1f1985e8 | ||
|
cebcc26baf | ||
|
75a12fc6f5 | ||
|
a3e0d89eb0 | ||
|
9594cc674f | ||
|
87cdee4fe8 | ||
|
1df55b78c2 | ||
|
f7820a23be | ||
|
f80b9cdca5 | ||
|
f2180b22c7 | ||
|
a745088213 | ||
|
14be7bd2b1 | ||
|
d453ea66da | ||
|
e7ae86e261 | ||
|
b7417f41c5 | ||
|
9e7aa381ed | ||
|
b02453e9f4 | ||
|
6b7f8fd31c | ||
|
4652242d6b | ||
|
a47ca2f357 | ||
|
e5a1e58159 | ||
|
01a1e34e99 | ||
|
e711220a66 | ||
|
1d20f529a0 | ||
|
79a94d25bd | ||
|
92793b8ff8 | ||
|
982024f359 | ||
|
e851bd4d61 | ||
|
e4b9383e96 | ||
|
95ac9aac14 | ||
|
b895eec69c | ||
|
1fe4e80f82 | ||
|
eb1f5f2632 | ||
|
114b792300 | ||
|
56a352a9d2 | ||
|
4c4d60dd83 | ||
|
134ab0fe98 | ||
|
bbd394272f | ||
|
8a2571f514 | ||
|
bf15be8144 | ||
|
bf6f8de7fa | ||
|
2e75e6ffb6 | ||
|
b987be54bf | ||
|
533e8a3742 | ||
|
9d007e64f6 | ||
|
8ec1578f50 | ||
|
4e8cc36d3a | ||
|
5a64cb2323 | ||
|
b2aba82a1b | ||
|
9d9500ba1b | ||
|
674c5a11c1 | ||
|
8e2b2947ae | ||
|
0413ca7cba | ||
|
15cf7800a4 | ||
|
db6114a4ee | ||
|
818495a697 | ||
|
7aa41c4050 | ||
|
1975b8af1d | ||
|
b870f5f4d1 | ||
|
3ebc720934 | ||
|
748976f393 | ||
|
83e5c7fadb | ||
|
f919a34166 | ||
|
77a9eca634 | ||
|
72b732a55d | ||
|
0236897d1f | ||
|
46f95e5e13 | ||
|
24287c0857 | ||
|
c930e4dd92 | ||
|
7d8856e4bc | ||
|
6d026bbf42 | ||
|
713770a448 | ||
|
ac873fa757 | ||
|
6b3513c1c4 | ||
|
840374c48c | ||
|
7f454f279c | ||
|
267c09f20c | ||
|
e44e77a3a6 | ||
|
4d9dd13ffb | ||
|
369e75cb7e | ||
|
0a6fa2431e | ||
|
0bc40d1748 | ||
|
919dc3cdea | ||
|
7c64b27ef4 | ||
|
369577a2c8 | ||
|
fbc25e5134 | ||
|
aba1628d36 | ||
|
90336e1edf | ||
|
dde32fcaee | ||
|
6324e79aba | ||
|
7a22f4b20f | ||
|
aad621bd84 | ||
|
0b4e1f3dee | ||
|
78f88db560 | ||
|
cc1c425ecf | ||
|
186290e355 | ||
|
f1f1d784ff | ||
|
a1c7efeb85 | ||
|
ba5f635687 | ||
|
bae5afc0da | ||
|
befd5c3b08 | ||
|
0b4e96a90f | ||
|
57edf38c1a | ||
|
502dd1ec1f | ||
|
02361ddfb2 | ||
|
bcf6cc1019 | ||
|
b97a2e7cf3 | ||
|
27158e1ee7 | ||
|
28abad0276 | ||
|
b59549ebe9 | ||
|
b9f788fbe8 | ||
|
9d89334cc5 | ||
|
6c67ff3fe8 | ||
|
6ef59f703a | ||
|
bbd055c798 | ||
|
53879fcefb | ||
|
fd6e7f3096 | ||
|
4d8cf41b7a | ||
|
2b0467e00f | ||
|
cef6646f50 | ||
|
b695f90ded | ||
|
d7f1246c32 | ||
|
739d1f2455 | ||
|
be7c6e700b | ||
|
320dd49a87 | ||
|
9bfa680fa4 | ||
|
ed32f9994d | ||
|
c6eb850abe | ||
|
6c458b81b2 | ||
|
6741f59aef | ||
|
6a0fd46fc4 | ||
|
b2c8beae71 | ||
|
b376327438 | ||
|
9eb4fecbe8 | ||
|
f672f4d1bb | ||
|
c8b085e963 | ||
|
8cd3daee9d | ||
|
0b03aec038 | ||
|
b04fd1a937 | ||
|
bdf4222d70 | ||
|
261f7ebbc2 | ||
|
91f25e9ec3 | ||
|
dd86bb88c6 | ||
|
0665f2de5f | ||
|
364e5df974 | ||
|
d3cdaccbc5 | ||
|
75dbc990e4 | ||
|
5bdd6e15e4 | ||
|
f7b5a2e971 | ||
|
a486eefd81 | ||
|
1fae364e7d | ||
|
76db0c41d3 | ||
|
7318b8917d | ||
|
fefcd682b8 | ||
|
a32c0b028b | ||
|
ffe62b8f8e | ||
|
5076374b5d | ||
|
d368e24f75 | ||
|
343c5eb587 | ||
|
f7fc379e56 | ||
|
ccf4c4bbb3 | ||
|
a32995abec | ||
|
abd87f3584 | ||
|
bbf7277abc | ||
|
b38b335fa1 | ||
|
cf41e71494 | ||
|
e75408d20d | ||
|
d79f3c6a80 | ||
|
1a5e196e4e | ||
|
138bfc8362 | ||
|
464795779f | ||
|
e65c80962d | ||
|
7171d1d6b2 | ||
|
162c6e95d3 | ||
|
63743656d4 | ||
|
a3a9032af7 | ||
|
194ef2b4ca | ||
|
89b8342ca0 | ||
|
ba32df2fb8 | ||
|
08234afe4f | ||
|
41eb28992e | ||
|
20a6da4944 | ||
|
544496a09b | ||
|
26e7d562aa | ||
|
d1814a4e0f | ||
|
a11adad23f | ||
|
834c8cc7d5 | ||
|
1801ea7873 | ||
|
b208634e40 | ||
|
7b3d071fd3 | ||
|
663a8bb06d | ||
|
6c34f083e3 | ||
|
6d9237c399 | ||
|
8a7186c1b1 | ||
|
c120686fae | ||
|
75c83e4117 | ||
|
d16b846d4e | ||
|
fe44c35406 | ||
|
376bcc4a0b | ||
|
26d9e63e83 | ||
|
a7ac2cee13 | ||
|
7a850704e5 | ||
|
d4cc561d90 | ||
|
1430935c7e | ||
|
497289fe69 | ||
|
f7d75b830c | ||
|
a20222747d | ||
|
75ac1e9770 | ||
|
64d606013d | ||
|
40e8e47398 | ||
|
6faf7e71ba | ||
|
b20abb824d | ||
|
fa8c209ea3 | ||
|
5181010d22 | ||
|
5c2a82b7ee | ||
|
f69de50ed1 | ||
|
56e142ac05 | ||
|
32c2d60f7e | ||
|
cb792cbb26 | ||
|
c36b10a416 | ||
|
f1d87cfbb2 | ||
|
cde6530832 | ||
|
a5b1fec6b1 | ||
|
7cf76d91a0 | ||
|
28dc314397 | ||
|
66b5e5dd3f | ||
|
678af5f744 | ||
|
85fcc28662 | ||
|
f6035de548 | ||
|
0ea82b9f98 | ||
|
3260e5127a | ||
|
f260d83923 | ||
|
cece720f68 | ||
|
03c0449a0b | ||
|
4ded7987e2 | ||
|
49bbf826b9 | ||
|
42fc52a067 | ||
|
72e97d57b2 | ||
|
dbd99e1f57 | ||
|
8e68824ac0 | ||
|
5fe79c4bad | ||
|
0fe89c34b3 | ||
|
4a451ad5ae | ||
|
27984ceef2 | ||
|
8987a2d177 | ||
|
915b6a918c | ||
|
40bf82552b | ||
|
211d17b609 | ||
|
6290bbdd4e | ||
|
aa9a3238a2 | ||
|
a303a60c4e | ||
|
7e67fa3076 | ||
|
7ef966536d | ||
|
4c3827158f | ||
|
6c577daf5d | ||
|
9e54c689a1 |
@ -58,5 +58,3 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
LARAVELS_LISTEN_IP=0.0.0.0
|
||||
LARAVELS_LISTEN_PORT=20000
|
||||
|
||||
DOCKER_ID=
|
||||
|
61
.github/workflows/electron.yml
vendored
Normal file
61
.github/workflows/electron.yml
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Create changelog text
|
||||
id: changelog
|
||||
uses: loopwerk/tag-changelog@v1
|
||||
with:
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
exclude_types: other,chore,build
|
||||
|
||||
- name: Create release
|
||||
uses: actions/create-release@latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
body: ${{ steps.changelog.outputs.changes }}
|
||||
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
environment: build
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-11]
|
||||
platform: [
|
||||
build-mac,
|
||||
build-mac-arm,
|
||||
build-win
|
||||
]
|
||||
|
||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_PAT }}
|
||||
EP_PRE_RELEASE: true
|
||||
run: ./cmd electron ${{ matrix.platform }}
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,7 +19,6 @@ Homestead.yaml
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
test.*
|
||||
composer.lock
|
||||
package-lock.json
|
||||
laravels-timer-process.pid
|
||||
.DS_Store
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "resources/drawio"]
|
||||
path = resources/drawio
|
||||
url = https://github.com/jgraph/drawio.git
|
71
README.md
71
README.md
@ -5,10 +5,15 @@ English | **[中文文档](./README_CN.md)**
|
||||
- [Screenshot Preview](README_PREVIEW.md)
|
||||
- [Demo site](http://www.dootask.com/)
|
||||
|
||||
**QQ Group**
|
||||
|
||||
Group No.: `546574618`
|
||||
|
||||
## Setup
|
||||
|
||||
> `Docker` & `Docker Compose` must be installed
|
||||
|
||||
- `Docker` & `Docker Compose v2.0+` must be installed
|
||||
- System: `Centos/Debian/Ubuntu/macOS`
|
||||
- Hardware suggestion: 2 cores and above 2G memory
|
||||
|
||||
### Deployment project
|
||||
|
||||
@ -16,30 +21,28 @@ English | **[中文文档](./README_CN.md)**
|
||||
# 1、Clone the repository
|
||||
|
||||
# Clone projects on github
|
||||
git clone https://github.com/kuaifan/dootask.git
|
||||
# or you can use gitee
|
||||
git clone https://gitee.com/aipaw/dootask.git
|
||||
git clone --depth=1 https://github.com/kuaifan/dootask.git
|
||||
# Or you can use gitee
|
||||
git clone --depth=1 https://gitee.com/aipaw/dootask.git
|
||||
|
||||
# 2、enter directory
|
||||
# 2、Enter directory
|
||||
cd dootask
|
||||
|
||||
# 3、Build project
|
||||
# 3、Installation(Custom port installation: ./cmd install --port 2222)
|
||||
./cmd install
|
||||
```
|
||||
Installed, project url: **`http://IP:PORT`**(`PORT`Default is`2222`)。
|
||||
|
||||
### Default Account
|
||||
### Reset password
|
||||
|
||||
```text
|
||||
account: admin@dootask.com
|
||||
password: 123456
|
||||
```bash
|
||||
# Reset default account password
|
||||
./cmd repassword
|
||||
```
|
||||
|
||||
### Change port
|
||||
|
||||
```bash
|
||||
./cmd php bin/run --port=2222
|
||||
./cmd up -d
|
||||
./cmd port 2222
|
||||
```
|
||||
|
||||
### Stop server
|
||||
@ -51,25 +54,40 @@ password: 123456
|
||||
./cmd start
|
||||
```
|
||||
|
||||
### Development compilation
|
||||
|
||||
```bash
|
||||
# Development mode, Mac OS only
|
||||
./cmd dev
|
||||
|
||||
# Production projects, macOS only
|
||||
./cmd prod
|
||||
```
|
||||
|
||||
### Shortcuts for running command
|
||||
|
||||
```bash
|
||||
# You can do this using the following command
|
||||
./cmd artisan "your command" // To run a artisan command
|
||||
./cmd php "your command" // To run a php command
|
||||
./cmd composer "your command" // To run a composer command
|
||||
./cmd supervisorctl "your command" // To run a supervisorctl command
|
||||
./cmd test "your command" // To run a phpunit command
|
||||
./cmd mysql "your command" // To run a mysql command (backup: Backup database, recovery: Restore database)
|
||||
./cmd artisan "your command" # To run a artisan command
|
||||
./cmd php "your command" # To run a php command
|
||||
./cmd nginx "your command" # To run a nginx command
|
||||
./cmd redis "your command" # To run a redis command
|
||||
./cmd composer "your command" # To run a composer command
|
||||
./cmd supervisorctl "your command" # To run a supervisorctl command
|
||||
./cmd test "your command" # To run a phpunit command
|
||||
./cmd mysql "your command" # To run a mysql command (backup: Backup database, recovery: Restore database)
|
||||
```
|
||||
|
||||
### NGINX OPEN HTTPS
|
||||
```
|
||||
// .env add
|
||||
APP_SCHEME=1
|
||||
### NGINX PROXY SSL
|
||||
|
||||
// nginx add
|
||||
```bash
|
||||
# 1、Nginx config add
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# 2、Enter directory and run command
|
||||
./cmd https
|
||||
```
|
||||
|
||||
## Upgrade
|
||||
@ -77,7 +95,7 @@ proxy_set_header X-Forwarded-Proto $scheme;
|
||||
**Note: Please back up your data before upgrading!**
|
||||
|
||||
```bash
|
||||
# Method 1: Enter directory and run command
|
||||
# Method 1: enter directory and run command
|
||||
./cmd update
|
||||
|
||||
# Or method 2: use this method if method 1 fails
|
||||
@ -86,6 +104,7 @@ git pull
|
||||
./cmd uninstall
|
||||
./cmd install
|
||||
./cmd mysql recovery
|
||||
./cmd artisan migrate
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
70
README_CN.md
70
README_CN.md
@ -5,10 +5,15 @@
|
||||
- [截图预览](README_PREVIEW.md)
|
||||
- [演示站点](http://www.dootask.com/)
|
||||
|
||||
**QQ交流群**
|
||||
|
||||
- QQ群号: `546574618`
|
||||
|
||||
## 安装程序
|
||||
|
||||
> 必须安装 `Docker` 和 `Docker Compose`
|
||||
|
||||
- 必须安装:`Docker` 和 `Docker Compose v2.0+`
|
||||
- 支持环境:`Centos/Debian/Ubuntu/macOS`
|
||||
- 硬件建议:2核2G以上
|
||||
|
||||
### 部署项目
|
||||
|
||||
@ -16,30 +21,28 @@
|
||||
# 1、克隆项目到您的本地或服务器
|
||||
|
||||
# 通过github克隆项目
|
||||
git clone https://github.com/kuaifan/dootask.git
|
||||
git clone --depth=1 https://github.com/kuaifan/dootask.git
|
||||
# 或者你也可以使用gitee
|
||||
git clone https://gitee.com/aipaw/dootask.git
|
||||
git clone --depth=1 https://gitee.com/aipaw/dootask.git
|
||||
|
||||
# 2、进入目录
|
||||
cd dootask
|
||||
|
||||
# 3、一键构建项目
|
||||
# 3、一键安装项目(自定义端口安装 ./cmd install --port 2222)
|
||||
./cmd install
|
||||
```
|
||||
安装完毕,项目地址为:**`http://IP:PORT`**(`PORT`默认为`2222`)。
|
||||
|
||||
### 默认账号
|
||||
### 重置密码
|
||||
|
||||
```text
|
||||
account: admin@dootask.com
|
||||
password: 123456
|
||||
```bash
|
||||
# 重置默认管理员密码
|
||||
./cmd repassword
|
||||
```
|
||||
|
||||
### 更换端口
|
||||
|
||||
```bash
|
||||
./cmd php bin/run --port=2222
|
||||
./cmd up -d
|
||||
./cmd port 2222
|
||||
```
|
||||
|
||||
### 停止服务
|
||||
@ -51,25 +54,41 @@ password: 123456
|
||||
./cmd start
|
||||
```
|
||||
|
||||
### 开发编译
|
||||
|
||||
```bash
|
||||
# 开发模式,仅限macOS
|
||||
./cmd dev
|
||||
|
||||
# 编译项目,仅限macOS
|
||||
./cmd prod
|
||||
```
|
||||
|
||||
|
||||
### 运行命令的快捷方式
|
||||
|
||||
```bash
|
||||
# 你可以使用以下命令来执行
|
||||
./cmd artisan "your command" // 运行 artisan 命令
|
||||
./cmd php "your command" // 运行 php 命令
|
||||
./cmd composer "your command" // 运行 composer 命令
|
||||
./cmd supervisorctl "your command" // 运行 supervisorctl 命令
|
||||
./cmd test "your command" // 运行 phpunit 命令
|
||||
./cmd mysql "your command" // 运行 mysql 命令 (backup: 备份数据库,recovery: 还原数据库)
|
||||
./cmd artisan "your command" # 运行 artisan 命令
|
||||
./cmd php "your command" # 运行 php 命令
|
||||
./cmd nginx "your command" # 运行 nginx 命令
|
||||
./cmd redis "your command" # 运行 redis 命令
|
||||
./cmd composer "your command" # 运行 composer 命令
|
||||
./cmd supervisorctl "your command" # 运行 supervisorctl 命令
|
||||
./cmd test "your command" # 运行 phpunit 命令
|
||||
./cmd mysql "your command" # 运行 mysql 命令 (backup: 备份数据库,recovery: 还原数据库)
|
||||
```
|
||||
|
||||
### 代理开启 HTTPS
|
||||
```
|
||||
// .env 文件添加
|
||||
APP_SCHEME=1
|
||||
### NGINX 代理 SSL
|
||||
|
||||
// nginx 代理配置添加
|
||||
```bash
|
||||
# 1、Nginx 代理配置添加
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# 2、进入项目所在目录,运行以下命令
|
||||
./cmd https
|
||||
```
|
||||
|
||||
## 升级更新
|
||||
@ -77,7 +96,7 @@ proxy_set_header X-Forwarded-Proto $scheme;
|
||||
**注意:在升级之前请备份好你的数据!**
|
||||
|
||||
```bash
|
||||
# 方法1:进入项目所在目录,运行一下命令即可
|
||||
# 方法1:进入项目所在目录,运行以下命令
|
||||
./cmd update
|
||||
|
||||
# (或者)方法2:如果方法1失败请使用此方法
|
||||
@ -86,11 +105,12 @@ git pull
|
||||
./cmd uninstall
|
||||
./cmd install
|
||||
./cmd mysql recovery
|
||||
./cmd artisan migrate
|
||||
```
|
||||
|
||||
## 卸载项目
|
||||
|
||||
```bash
|
||||
# 进入项目所在目录,运行一下命令即可
|
||||
# 进入项目所在目录,运行以下命令
|
||||
./cmd uninstall
|
||||
```
|
||||
|
@ -5,6 +5,7 @@ namespace App\Exceptions;
|
||||
use App\Module\Base;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
@ -57,4 +58,18 @@ class Handler extends ExceptionHandler
|
||||
}
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写report优雅记录
|
||||
* @param Throwable $e
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function report(Throwable $e)
|
||||
{
|
||||
if ($e instanceof ApiException) {
|
||||
Log::error($e->getMessage(), ['exception' => ' at ' . $e->getFile() .':' . $e->getLine()]);
|
||||
} else {
|
||||
parent::report($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ if (!function_exists('asset_main')) {
|
||||
if (!function_exists('seeders_at')) {
|
||||
function seeders_at($data)
|
||||
{
|
||||
$diff = time() - strtotime("2021-07-01");
|
||||
$diff = time() - strtotime("2021-07-02");
|
||||
$time = strtotime($data) + $diff;
|
||||
return date("Y-m-d H:i:s", $time);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\ProjectTask;
|
||||
use App\Models\ProjectTaskFile;
|
||||
use App\Models\User;
|
||||
@ -10,7 +11,9 @@ use App\Models\WebSocketDialogMsg;
|
||||
use App\Models\WebSocketDialogMsgRead;
|
||||
use App\Models\WebSocketDialogUser;
|
||||
use App\Module\Base;
|
||||
use Carbon\Carbon;
|
||||
use Request;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
* @apiDefine dialog
|
||||
@ -20,18 +23,28 @@ use Request;
|
||||
class DialogController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* 对话列表
|
||||
* @api {get} api/dialog/lists 01. 对话列表
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName lists
|
||||
*
|
||||
* @apiParam {Number} [page] 当前页,默认:1
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:100,最大:200
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$list = WebSocketDialog::select(['web_socket_dialogs.*'])
|
||||
$list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at'])
|
||||
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
|
||||
->where('u.userid', $user->userid)
|
||||
->orderByDesc('u.top_at')
|
||||
->orderByDesc('web_socket_dialogs.last_at')
|
||||
->paginate(Base::getPaginate(200, 100));
|
||||
$list->transform(function (WebSocketDialog $item) use ($user) {
|
||||
@ -42,9 +55,18 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个会话信息
|
||||
* @api {get} api/dialog/one 02. 获取单个会话信息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName one
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function one()
|
||||
{
|
||||
@ -65,9 +87,18 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开会话
|
||||
* @api {get} api/dialog/msg/user 03. 打开会话
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName open__user
|
||||
*
|
||||
* @apiParam {Number} userid 对话会员ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function open__user()
|
||||
{
|
||||
@ -90,12 +121,21 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息列表
|
||||
* @api {get} api/dialog/msg/lists 04. 获取消息列表
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__lists
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
*
|
||||
* @apiParam {Number} [page] 当前页,默认:1
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:50,最大:100
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__lists()
|
||||
{
|
||||
@ -124,28 +164,63 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* 未读消息
|
||||
* @api {get} api/dialog/msg/unread 05. 获取未读消息数量
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__unread
|
||||
*
|
||||
* @apiParam {Number} [dialog_id] 对话ID,留空获取总未读消息数量
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__unread()
|
||||
{
|
||||
$unread = WebSocketDialogMsgRead::whereUserid(User::userid())->whereReadAt(null)->count();
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
//
|
||||
$builder = WebSocketDialogMsgRead::whereUserid(User::userid())->whereReadAt(null);
|
||||
if ($dialog_id > 0) {
|
||||
$builder->whereDialogId($dialog_id);
|
||||
}
|
||||
$unread = $builder->count();
|
||||
return Base::retSuccess('success', [
|
||||
'unread' => $unread,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @api {post} api/dialog/msg/sendtext 06. 发送消息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__sendtext
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
* @apiParam {String} text 消息内容
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__sendtext()
|
||||
{
|
||||
Base::checkClientVersion('0.8.1');
|
||||
$user = User::auth();
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
$text = trim(Request::input('text'));
|
||||
$chat_nickname = Base::settingFind('system', 'chat_nickname');
|
||||
if ($chat_nickname == 'required') {
|
||||
$nickname = User::select(['nickname as nickname_original'])->whereUserid($user->userid)->value('nickname_original');
|
||||
if (empty($nickname)) {
|
||||
return Base::retError('请设置昵称', [], -2);
|
||||
}
|
||||
}
|
||||
//
|
||||
$dialog_id = Base::getPostInt('dialog_id');
|
||||
$text = trim(Base::getPostValue('text'));
|
||||
//
|
||||
if (mb_strlen($text) < 1) {
|
||||
return Base::retError('消息内容不能为空');
|
||||
@ -155,20 +230,39 @@ class DialogController extends AbstractController
|
||||
//
|
||||
WebSocketDialog::checkDialog($dialog_id);
|
||||
//
|
||||
$msg = [
|
||||
'text' => $text
|
||||
];
|
||||
if (mb_strlen($text) > 2000) {
|
||||
$array = mb_str_split($text, 2000);
|
||||
} else {
|
||||
$array = [$text];
|
||||
}
|
||||
//
|
||||
return WebSocketDialogMsg::sendMsg($dialog_id, 'text', $msg, $user->userid);
|
||||
$list = [];
|
||||
foreach ($array as $item) {
|
||||
$res = WebSocketDialogMsg::sendMsg($dialog_id, 'text', ['text' => $item], $user->userid);
|
||||
if (Base::isSuccess($res)) {
|
||||
$list[] = $res['data'];
|
||||
}
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('发送成功', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* {post}文件上传
|
||||
* @api {post} api/dialog/msg/sendfile 07. 文件上传
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__sendfile
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
* @apiParam {String} [filename] post-文件名称
|
||||
* @apiParam {String} [image64] post-base64图片(二选一)
|
||||
* @apiParam {File} [files] post-文件对象(二选一)
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__sendfile()
|
||||
{
|
||||
@ -178,7 +272,7 @@ class DialogController extends AbstractController
|
||||
//
|
||||
$dialog = WebSocketDialog::checkDialog($dialog_id);
|
||||
//
|
||||
$path = "uploads/chat/" . $user->userid . "/";
|
||||
$path = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/";
|
||||
$image64 = Base::getPostValue('image64');
|
||||
$fileName = Base::getPostValue('filename');
|
||||
if ($image64) {
|
||||
@ -190,7 +284,7 @@ class DialogController extends AbstractController
|
||||
} else {
|
||||
$data = Base::upload([
|
||||
"file" => Request::file('files'),
|
||||
"type" => 'file',
|
||||
"type" => 'more',
|
||||
"path" => $path,
|
||||
"fileName" => $fileName,
|
||||
]);
|
||||
@ -233,9 +327,18 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息阅读情况
|
||||
* @api {get} api/dialog/msg/readlist 08. 获取消息阅读情况
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__readlist
|
||||
*
|
||||
* @apiParam {Number} msg_id 消息ID(需要是消息的发送人)
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__readlist()
|
||||
{
|
||||
@ -251,4 +354,137 @@ class DialogController extends AbstractController
|
||||
$read = WebSocketDialogMsgRead::whereMsgId($msg_id)->get();
|
||||
return Base::retSuccess('success', $read ?: []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/detail 09. 消息详情
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__detail
|
||||
*
|
||||
* @apiParam {Number} msg_id 消息ID
|
||||
* @apiParam {String} only_update_at 仅获取update_at字段
|
||||
* - no (默认)
|
||||
* - yes
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__detail()
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
$msg_id = intval(Request::input('msg_id'));
|
||||
$only_update_at = Request::input('only_update_at', 'no');
|
||||
//
|
||||
$dialogMsg = WebSocketDialogMsg::whereId($msg_id)->first();
|
||||
if (empty($dialogMsg)) {
|
||||
return Base::retError("文件不存在");
|
||||
}
|
||||
//
|
||||
if ($only_update_at == 'yes') {
|
||||
return Base::retSuccess('success', [
|
||||
'id' => $dialogMsg->id,
|
||||
'update_at' => Carbon::parse($dialogMsg->updated_at)->toDateTimeString()
|
||||
]);
|
||||
}
|
||||
//
|
||||
$data = $dialogMsg->toArray();
|
||||
//
|
||||
if ($data['type'] == 'file') {
|
||||
$msg = Base::json2array($dialogMsg->getRawOriginal('msg'));
|
||||
$msg = File::formatFileData($msg);
|
||||
$data['content'] = $msg['content'];
|
||||
$data['file_mode'] = $msg['file_mode'];
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/download 10. 文件下载
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__download
|
||||
*
|
||||
* @apiParam {Number} msg_id 消息ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__download()
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
$msg_id = intval(Request::input('msg_id'));
|
||||
//
|
||||
$msg = WebSocketDialogMsg::whereId($msg_id)->first();
|
||||
if (empty($msg)) {
|
||||
abort(403, "This file not exist.");
|
||||
}
|
||||
if ($msg->type != 'file') {
|
||||
abort(403, "This file not support download.");
|
||||
}
|
||||
$array = Base::json2array($msg->getRawOriginal('msg'));
|
||||
//
|
||||
return Response::download(public_path($array['path']), $array['name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/withdraw 11. 聊天消息撤回
|
||||
*
|
||||
* @apiDescription 消息撤回限制24小时内,需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__withdraw
|
||||
*
|
||||
* @apiParam {Number} msg_id 消息ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__withdraw()
|
||||
{
|
||||
$user = User::auth();
|
||||
$msg_id = intval(Request::input("msg_id"));
|
||||
$msg = WebSocketDialogMsg::whereId($msg_id)->whereUserid($user->userid)->first();
|
||||
if (empty($msg)) {
|
||||
return Base::retError("消息不存在或已被删除");
|
||||
}
|
||||
$msg->deleteMsg();
|
||||
return Base::retSuccess("success");
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/top 12. 会话置顶
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName top
|
||||
*
|
||||
* @apiParam {Number} dialog_id 会话ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function top()
|
||||
{
|
||||
$user = User::auth();
|
||||
$dialogId = intval(Request::input('dialog_id'));
|
||||
$dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first();
|
||||
if (!$dialogUser) {
|
||||
return Base::retError("会话不存在");
|
||||
}
|
||||
$dialogUser->top_at = $dialogUser->top_at ? null : Carbon::now();
|
||||
$dialogUser->save();
|
||||
return Base::retSuccess("success", $dialogId);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
466
app/Http/Controllers/Api/ReportController.php
Executable file
466
app/Http/Controllers/Api/ReportController.php
Executable file
@ -0,0 +1,466 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Models\ProjectTask;
|
||||
use App\Models\Report;
|
||||
use App\Models\ReportReceive;
|
||||
use App\Models\User;
|
||||
use App\Module\Base;
|
||||
use App\Tasks\PushTask;
|
||||
use Carbon\Carbon;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
/**
|
||||
* @apiDefine report
|
||||
*
|
||||
* 汇报
|
||||
*/
|
||||
class ReportController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @api {get} api/report/my 01. 我发送的汇报
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup report
|
||||
* @apiName my
|
||||
*
|
||||
* @apiParam {String} [type] 汇报类型,weekly:周报,daily:日报
|
||||
* @apiParam {Array} [created_at] 汇报时间
|
||||
* @apiParam {Number} [page] 当前页,默认:1
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:20,最大:50
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function my(): array
|
||||
{
|
||||
$user = User::auth();
|
||||
// 搜索当前用户
|
||||
$builder = Report::with(['receivesUser'])->whereUserid($user->userid);
|
||||
$type = trim(Request::input('type'));
|
||||
$createAt = Request::input('created_at');
|
||||
in_array($type, [Report::WEEKLY, Report::DAILY]) && $builder->whereType($type);
|
||||
$whereArray = [];
|
||||
if (is_array($createAt)) {
|
||||
if ($createAt[0] > 0) $whereArray[] = ['created_at', '>=', date('Y-m-d H:i:s', Base::dayTimeF($createAt[0]))];
|
||||
if ($createAt[1] > 0) $whereArray[] = ['created_at', '<=', date('Y-m-d H:i:s', Base::dayTimeE($createAt[1]))];
|
||||
}
|
||||
$list = $builder->where($whereArray)->orderByDesc('created_at')->paginate(Base::getPaginate(50, 20));
|
||||
return Base::retSuccess('success', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/report/receive 02. 我接收的汇报
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup report
|
||||
* @apiName receive
|
||||
*
|
||||
* @apiParam {String} [username] 会员名
|
||||
* @apiParam {String} [type] 汇报类型,weekly:周报,daily:日报
|
||||
* @apiParam {Array} [created_at] 汇报时间
|
||||
* @apiParam {Number} [page] 当前页,默认:1
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:20,最大:50
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function receive(): array
|
||||
{
|
||||
$user = User::auth();
|
||||
$builder = Report::with(['receivesUser']);
|
||||
$builder->whereHas("receivesUser", function ($query) use ($user) {
|
||||
$query->where("report_receives.userid", $user->userid);
|
||||
});
|
||||
$type = trim(Request::input('type'));
|
||||
$createAt = Request::input('created_at');
|
||||
$username = trim(Request::input('username', ''));
|
||||
$builder->whereHas('sendUser', function ($query) use ($username) {
|
||||
if (!empty($username)) {
|
||||
$query->where('users.email', 'LIKE', '%' . $username . '%');
|
||||
}
|
||||
});
|
||||
in_array($type, [Report::WEEKLY, Report::DAILY]) && $builder->whereType($type);
|
||||
$whereArray = [];
|
||||
if (is_array($createAt)) {
|
||||
if ($createAt[0] > 0) $whereArray[] = ['created_at', '>=', date('Y-m-d H:i:s', Base::dayTimeF($createAt[0]))];
|
||||
if ($createAt[1] > 0) $whereArray[] = ['created_at', '<=', date('Y-m-d H:i:s', Base::dayTimeE($createAt[1]))];
|
||||
}
|
||||
$list = $builder->where($whereArray)->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");
|
||||
}
|
||||
}
|
||||
return Base::retSuccess('success', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/report/store 03. 保存并发送工作汇报
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup report
|
||||
* @apiName store
|
||||
*
|
||||
* @apiParam {Number} [id] 汇报ID
|
||||
* @apiParam {String} [title] 汇报标题
|
||||
* @apiParam {Array} [type] 汇报类型,weekly:周报,daily:日报
|
||||
* @apiParam {Number} [content] 内容
|
||||
* @apiParam {Number} [receive] 汇报对象
|
||||
* @apiParam {Number} [offset] 偏移量
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function store(): array
|
||||
{
|
||||
$input = [
|
||||
"id" => Base::getPostValue("id", 0),
|
||||
"title" => Base::getPostValue("title"),
|
||||
"type" => Base::getPostValue("type"),
|
||||
"content" => Base::getPostValue("content"),
|
||||
"receive" => Base::getPostValue("receive"),
|
||||
// 以当前日期为基础的周期偏移量。例如选择了上一周那么就是 -1,上一天同理。
|
||||
"offset" => Base::getPostValue("offset", 0),
|
||||
];
|
||||
$validator = Validator::make($input, [
|
||||
'id' => 'numeric',
|
||||
'title' => 'required',
|
||||
'type' => ['required', Rule::in([Report::WEEKLY, Report::DAILY])],
|
||||
'content' => 'required',
|
||||
'receive' => 'required',
|
||||
'offset' => ['numeric', 'max:0'],
|
||||
], [
|
||||
'id.numeric' => 'ID只能是数字',
|
||||
'title.required' => '请填写标题',
|
||||
'type.required' => '请选择汇报类型',
|
||||
'type.in' => '汇报类型错误',
|
||||
'content.required' => '请填写汇报内容',
|
||||
'receive.required' => '请选择接收人',
|
||||
'offset.numeric' => '工作汇报周期格式错误,只能是数字',
|
||||
'offset.max' => '只能提交当天/本周或者之前的的工作汇报',
|
||||
]);
|
||||
if ($validator->fails())
|
||||
return Base::retError($validator->errors()->first());
|
||||
|
||||
$user = User::auth();
|
||||
// 接收人
|
||||
if (is_array($input["receive"])) {
|
||||
// 删除当前登录人
|
||||
$input["receive"] = array_diff($input["receive"], [$user->userid]);
|
||||
|
||||
// 查询用户是否存在
|
||||
if (count($input["receive"]) !== User::whereIn("userid", $input["receive"])->count())
|
||||
return Base::retError("用户不存在");
|
||||
|
||||
foreach ($input["receive"] as $userid) {
|
||||
$input["receive_content"][] = [
|
||||
"receive_time" => Carbon::now()->toDateTimeString(),
|
||||
"userid" => $userid,
|
||||
"read" => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 在事务中运行
|
||||
Report::transaction(function () use ($input, $user) {
|
||||
$id = $input["id"];
|
||||
if ($id) {
|
||||
// 编辑
|
||||
$report = Report::getOne($id);
|
||||
$report->updateInstance([
|
||||
"title" => $input["title"],
|
||||
"type" => $input["type"],
|
||||
"content" => htmlspecialchars($input["content"]),
|
||||
]);
|
||||
} else {
|
||||
// 生成唯一标识
|
||||
$sign = Report::generateSign($input["type"], $input["offset"]);
|
||||
// 检查唯一标识是否存在
|
||||
if (empty($input["id"])) {
|
||||
if (Report::query()->whereSign($sign)->whereType($input["type"])->count() > 0)
|
||||
throw new ApiException("请勿重复提交工作汇报");
|
||||
}
|
||||
$report = Report::createInstance([
|
||||
"title" => $input["title"],
|
||||
"type" => $input["type"],
|
||||
"content" => htmlspecialchars($input["content"]),
|
||||
"userid" => $user->userid,
|
||||
"sign" => $sign,
|
||||
]);
|
||||
}
|
||||
|
||||
$report->save();
|
||||
if (!empty($input["receive_content"])) {
|
||||
// 删除关联
|
||||
$report->Receives()->delete();
|
||||
// 保存接收人
|
||||
$report->Receives()->createMany($input["receive_content"]);
|
||||
}
|
||||
|
||||
// 推送消息
|
||||
$userids = [];
|
||||
foreach ($input["receive_content"] as $item) {
|
||||
$userids[] = $item['userid'];
|
||||
}
|
||||
if ($userids) {
|
||||
$params = [
|
||||
'ignoreFd' => Request::header('fd'),
|
||||
'userid' => $userids,
|
||||
'msg' => [
|
||||
'type' => 'report',
|
||||
'action' => 'unreadUpdate',
|
||||
]
|
||||
];
|
||||
Task::deliver(new PushTask($params, false));
|
||||
}
|
||||
});
|
||||
return Base::retSuccess('保存成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/report/template 04. 生成汇报模板
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup report
|
||||
* @apiName template
|
||||
*
|
||||
* @apiParam {Array} [type] 汇报类型,weekly:周报,daily:日报
|
||||
* @apiParam {Number} [offset] 偏移量
|
||||
* @apiParam {String} [date] 时间
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function template(): array
|
||||
{
|
||||
$user = User::auth();
|
||||
$type = trim(Request::input("type"));
|
||||
$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();
|
||||
if ($offset > 0) {
|
||||
// 将当前时间调整为偏移量当天结束
|
||||
$now_dt->subDays($offset)->endOfDay();
|
||||
// 开始时间偏移量计算
|
||||
$start_time->subDays($offset);
|
||||
}
|
||||
$end_time = Carbon::instance($start_time)->endOfDay();
|
||||
} else {
|
||||
$start_time = Carbon::now();
|
||||
if ($offset > 0) {
|
||||
// 将当前时间调整为偏移量当周结束
|
||||
$now_dt->subWeeks($offset)->endOfDay();
|
||||
// 开始时间偏移量计算
|
||||
$start_time->subWeeks($offset);
|
||||
}
|
||||
$start_time->startOfWeek();
|
||||
$end_time = Carbon::instance($start_time)->endOfWeek();
|
||||
}
|
||||
// 生成唯一标识
|
||||
$sign = Report::generateSign($type, 0, Carbon::instance($start_time));
|
||||
$one = Report::query()->whereSign($sign)->whereType($type)->first();
|
||||
// 如果已经提交了相关汇报
|
||||
if ($one && $id > 0) {
|
||||
return Base::retSuccess('success', [
|
||||
"content" => $one->content,
|
||||
"title" => $one->title,
|
||||
"id" => $one->id,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// 已完成的任务
|
||||
$completeContent = "";
|
||||
$complete_task = ProjectTask::query()
|
||||
->whereNotNull("complete_at")
|
||||
->whereBetween("complete_at", [$start_time->toDateTimeString(), $end_time->toDateTimeString()])
|
||||
->whereHas("taskUser", function ($query) use ($user) {
|
||||
$query->where("userid", $user->userid);
|
||||
})
|
||||
->orderByDesc("id")
|
||||
->get();
|
||||
if ($complete_task->isNotEmpty()) {
|
||||
foreach ($complete_task as $task) {
|
||||
$complete_at = Carbon::parse($task->complete_at);
|
||||
$pre = $type == Report::WEEKLY ? ('<span>[' . Base::Lang('周' . ['日', '一', '二', '三', '四', '五', '六'][$complete_at->dayOfWeek]) . ']</span> ') : '';
|
||||
$completeContent .= '<li>' . $pre . $task->name . '</li>';
|
||||
}
|
||||
} else {
|
||||
$completeContent = '<li> </li>';
|
||||
}
|
||||
|
||||
// 未完成的任务
|
||||
$unfinishedContent = "";
|
||||
$unfinished_task = ProjectTask::query()
|
||||
->whereNull("complete_at")
|
||||
->whereNotNull("start_at")
|
||||
->where("end_at", "<", $end_time->toDateTimeString())
|
||||
->whereHas("taskUser", function ($query) use ($user) {
|
||||
$query->where("userid", $user->userid);
|
||||
})
|
||||
->orderByDesc("id")
|
||||
->get();
|
||||
if ($unfinished_task->isNotEmpty()) {
|
||||
foreach ($unfinished_task as $task) {
|
||||
empty($task->end_at) || $end_at = Carbon::parse($task->end_at);
|
||||
$pre = (!empty($end_at) && $end_at->lt($now_dt)) ? '<span style="color:#ff0000;">[' . Base::Lang('超期') . ']</span> ' : '';
|
||||
$unfinishedContent .= '<li>' . $pre . $task->name . '</li>';
|
||||
}
|
||||
} else {
|
||||
$unfinishedContent = '<li> </li>';
|
||||
}
|
||||
// 生成标题
|
||||
if ($type === Report::WEEKLY) {
|
||||
$title = $user->nickname . "的周报[" . $start_time->format("m/d") . "-" . $end_time->format("m/d") . "]";
|
||||
$title .= "[" . $start_time->month . "月第" . $start_time->weekOfMonth . "周]";
|
||||
} else {
|
||||
$title = $user->nickname . "的日报[" . $start_time->format("Y/m/d") . "]";
|
||||
}
|
||||
$data = [
|
||||
"time" => $start_time->toDateTimeString(),
|
||||
"complete_task" => $complete_task,
|
||||
"unfinished_task" => $unfinished_task,
|
||||
"content" => '<h2>' . Base::Lang('已完成工作') . '</h2><ol>' .
|
||||
$completeContent . '</ol><h2>' .
|
||||
Base::Lang('未完成的工作') . '</h2><ol>' .
|
||||
$unfinishedContent . '</ol>',
|
||||
"title" => $title,
|
||||
];
|
||||
if ($one) {
|
||||
$data['id'] = $one->id;
|
||||
}
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/report/detail 05. 报告详情
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup report
|
||||
* @apiName detail
|
||||
*
|
||||
* @apiParam {Number} [id] 报告id
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function detail(): array
|
||||
{
|
||||
$id = intval(trim(Request::input("id")));
|
||||
if (empty($id))
|
||||
return Base::retError("缺少ID参数");
|
||||
|
||||
$one = Report::getOne($id);
|
||||
$one->type_val = $one->getRawOriginal("type");
|
||||
|
||||
$user = User::auth();
|
||||
// 标记为已读
|
||||
if (!empty($one->receivesUser)) {
|
||||
foreach ($one->receivesUser as $item) {
|
||||
if ($item->userid === $user->userid && $item->pivot->read === 0) {
|
||||
$one->receivesUser()->updateExistingPivot($user->userid, [
|
||||
"read" => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Base::retSuccess("success", $one);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/report/last_submitter 06. 获取最后一次提交的接收人
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup report
|
||||
* @apiName last_submitter
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function last_submitter(): array
|
||||
{
|
||||
$one = Report::getLastOne();
|
||||
return Base::retSuccess("success", empty($one["receives"]) ? [] : $one["receives"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/report/unread 07. 获取未读
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup report
|
||||
* @apiName unread
|
||||
*
|
||||
* @apiParam {Number} [userid] 用户id
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function unread(): array
|
||||
{
|
||||
$userid = intval(trim(Request::input("userid")));
|
||||
$user = empty($userid) ? User::auth() : User::find($userid);
|
||||
|
||||
$data = Report::whereHas("Receives", function (Builder $query) use ($user) {
|
||||
$query->where("userid", $user->userid)->where("read", 0);
|
||||
})->orderByDesc('created_at')->paginate(Base::getPaginate(50, 20));
|
||||
return Base::retSuccess("success", $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/report/read 08. 标记汇报已读,可批量
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup report
|
||||
* @apiName read
|
||||
*
|
||||
* @apiParam {String} [ids] 报告id
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function read(): array
|
||||
{
|
||||
$user = User::auth();
|
||||
$ids = Request::input("ids");
|
||||
if (!is_array($ids) && !is_string($ids)) {
|
||||
return Base::retError("请传入正确的工作汇报Id");
|
||||
}
|
||||
|
||||
if (is_string($ids)) {
|
||||
$ids = explode(",", $ids);
|
||||
}
|
||||
|
||||
$data = Report::with(["receivesUser" => function (BelongsToMany $query) use ($user) {
|
||||
$query->where("report_receives.userid", $user->userid)->where("read", 0);
|
||||
}])->whereIn("id", $ids)->get();
|
||||
|
||||
if ($data->isNotEmpty()) {
|
||||
foreach ($data as $item) {
|
||||
(!empty($item->receivesUser) && $item->receivesUser->isNotEmpty()) && $item->receivesUser()->updateExistingPivot($user->userid, [
|
||||
"read" => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
return Base::retSuccess("success", $data);
|
||||
}
|
||||
}
|
@ -24,7 +24,8 @@ class SystemController extends AbstractController
|
||||
*
|
||||
* @apiParam {String} type
|
||||
* - get: 获取(默认)
|
||||
* - save: 保存设置(参数:reg、login_code)
|
||||
* - all: 获取所有(需要管理员权限)
|
||||
* - save: 保存设置(参数:reg、reg_invite、login_code、password_policy、project_invite、chat_nickname、auto_archived、archived_day)
|
||||
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@ -37,32 +38,79 @@ class SystemController extends AbstractController
|
||||
if (env("SYSTEM_SETTING") == 'disabled') {
|
||||
return Base::retError('当前环境禁止修改');
|
||||
}
|
||||
$user = User::auth();
|
||||
$user->isAdmin();
|
||||
User::auth('admin');
|
||||
$all = Request::input();
|
||||
foreach ($all AS $key => $value) {
|
||||
if (!in_array($key, ['reg', 'login_code'])) {
|
||||
if (!in_array($key, ['reg', 'reg_invite', 'login_code', 'password_policy', 'project_invite', 'chat_nickname', 'auto_archived', 'archived_day'])) {
|
||||
unset($all[$key]);
|
||||
}
|
||||
}
|
||||
$all['archived_day'] = floatval($all['archived_day']);
|
||||
if ($all['auto_archived'] == 'open') {
|
||||
if ($all['archived_day'] <= 0) {
|
||||
return Base::retError('自动归档时间不可小于1天!');
|
||||
} elseif ($all['archived_day'] > 100) {
|
||||
return Base::retError('自动归档时间不可大于100天!');
|
||||
}
|
||||
}
|
||||
$setting = Base::setting('system', Base::newTrim($all));
|
||||
} else {
|
||||
$setting = Base::setting('system');
|
||||
}
|
||||
//
|
||||
if ($type == 'all' || $type == 'save') {
|
||||
User::auth('admin');
|
||||
$setting['reg_invite'] = $setting['reg_invite'] ?: Base::generatePassword(8);
|
||||
} else {
|
||||
if (isset($setting['reg_invite'])) unset($setting['reg_invite']);
|
||||
}
|
||||
//
|
||||
$setting['reg'] = $setting['reg'] ?: 'open';
|
||||
$setting['login_code'] = $setting['login_code'] ?: 'auto';
|
||||
$setting['password_policy'] = $setting['password_policy'] ?: 'simple';
|
||||
$setting['project_invite'] = $setting['project_invite'] ?: 'open';
|
||||
$setting['chat_nickname'] = $setting['chat_nickname'] ?: 'optional';
|
||||
$setting['auto_archived'] = $setting['auto_archived'] ?: 'close';
|
||||
$setting['archived_day'] = floatval($setting['archived_day']) ?: 7;
|
||||
//
|
||||
return Base::retSuccess('success', $setting ?: json_decode('{}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/system/priority 02. 获取优先级、保存优先级
|
||||
* @api {get} api/system/demo 02. 获取演示账号
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName demo
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function demo()
|
||||
{
|
||||
$demo_account = env('DEMO_ACCOUNT');
|
||||
$demo_password = env('DEMO_PASSWORD');
|
||||
if (empty($demo_account) || empty($demo_password)) {
|
||||
return Base::retError('No demo account');
|
||||
}
|
||||
return Base::retSuccess('success', [
|
||||
'account' => $demo_account,
|
||||
'password' => $demo_password,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/system/priority 03. 任务优先级
|
||||
*
|
||||
* @apiDescription 获取任务优先级、保存任务优先级
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName priority
|
||||
*
|
||||
* @apiParam {String} type
|
||||
* - get: 获取(默认)
|
||||
* - save: 保存(限管理员)
|
||||
* @apiParam {Array} list 优先级数据,格式:[{name,color,days,priority}]
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
@ -73,15 +121,14 @@ class SystemController extends AbstractController
|
||||
{
|
||||
$type = trim(Request::input('type'));
|
||||
if ($type == 'save') {
|
||||
$user = User::auth();
|
||||
$user->isAdmin();
|
||||
User::auth('admin');
|
||||
$list = Base::getPostValue('list');
|
||||
$array = [];
|
||||
if (empty($list) || !is_array($list)) {
|
||||
return Base::retError('参数错误');
|
||||
}
|
||||
foreach ($list AS $item) {
|
||||
if (empty($item['name']) || empty($item['color']) || empty($item['days']) || empty($item['priority'])) {
|
||||
if (empty($item['name']) || empty($item['color']) || empty($item['priority'])) {
|
||||
continue;
|
||||
}
|
||||
$array[] = [
|
||||
@ -103,7 +150,54 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/info 03. 获取终端详细信息
|
||||
* @api {post} api/system/column/template 04. 创建项目模板
|
||||
*
|
||||
* @apiDescription 获取创建项目模板、保存创建项目模板
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName column__template
|
||||
*
|
||||
* @apiParam {String} type
|
||||
* - get: 获取(默认)
|
||||
* - save: 保存(限管理员)
|
||||
* @apiParam {Array} list 优先级数据,格式:[{name,columns}]
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function column__template()
|
||||
{
|
||||
$type = trim(Request::input('type'));
|
||||
if ($type == 'save') {
|
||||
User::auth('admin');
|
||||
$list = Base::getPostValue('list');
|
||||
$array = [];
|
||||
if (empty($list) || !is_array($list)) {
|
||||
return Base::retError('参数错误');
|
||||
}
|
||||
foreach ($list AS $item) {
|
||||
if (empty($item['name']) || empty($item['columns'])) {
|
||||
continue;
|
||||
}
|
||||
$array[] = [
|
||||
'name' => $item['name'],
|
||||
'columns' => array_values(array_filter(array_unique(explode(",", $item['columns']))))
|
||||
];
|
||||
}
|
||||
if (empty($array)) {
|
||||
return Base::retError('参数为空');
|
||||
}
|
||||
$setting = Base::setting('columnTemplate', $array);
|
||||
} else {
|
||||
$setting = Base::setting('columnTemplate');
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $setting);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/info 05. 获取终端详细信息
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@ -132,7 +226,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/ip 04. 获取IP地址
|
||||
* @api {get} api/system/get/ip 06. 获取IP地址
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@ -147,7 +241,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/cnip 05. 是否中国IP地址
|
||||
* @api {get} api/system/get/cnip 07. 是否中国IP地址
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@ -164,7 +258,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/ipgcj02 06. 获取IP地址经纬度
|
||||
* @api {get} api/system/get/ipgcj02 08. 获取IP地址经纬度
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@ -181,7 +275,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/ipinfo 07. 获取IP地址详细信息
|
||||
* @api {get} api/system/get/ipinfo 09. 获取IP地址详细信息
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@ -197,81 +291,6 @@ class SystemController extends AbstractController
|
||||
return Base::getIpInfo(Request::input("ip"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/appinfo 08. 获取应用下载信息
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName get__appinfo
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function get__appinfo() {
|
||||
$array = [
|
||||
'name' => '',
|
||||
'version' => '',
|
||||
'list' => [],
|
||||
];
|
||||
//
|
||||
$file = base_path("electron/package.json");
|
||||
$dist = base_path("electron/dist");
|
||||
if (file_exists($file)) {
|
||||
$packageArray = json_decode(file_get_contents($file), true);
|
||||
$array['name'] = $packageArray['name'] ?? 'No app';
|
||||
$array['version'] = $packageArray['version'] ?? '';
|
||||
//
|
||||
$list = [
|
||||
[
|
||||
'icon' => 'logo-apple',
|
||||
'name' => 'macOS Intel',
|
||||
'file' => "{$array['name']}-{$array['version']}.dmg"
|
||||
],
|
||||
[
|
||||
'icon' => 'logo-apple',
|
||||
'name' => 'macOS M1',
|
||||
'file' => "{$array['name']}-{$array['version']}-arm64.dmg"
|
||||
],
|
||||
[
|
||||
'icon' => 'logo-windows',
|
||||
'name' => 'Windows x64',
|
||||
'file' => "{$array['name']} Setup {$array['version']}.exe"
|
||||
]
|
||||
];
|
||||
foreach ($list as $item) {
|
||||
if (file_exists("{$dist}/{$item['file']}")) {
|
||||
$item['url'] = Base::fillUrl('api/system/get/appdown?file=' . urlencode($item['file']));
|
||||
$item['size'] = filesize("{$dist}/{$item['file']}");
|
||||
$array['list'][] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
if (count($array['list']) == 0) {
|
||||
return Base::retError('No file');
|
||||
}
|
||||
return Base::retSuccess('success', $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/appdown 09. 下载应用
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName get__appdown
|
||||
*
|
||||
* @apiParam {String} file 文件名称
|
||||
*/
|
||||
public function get__appdown() {
|
||||
$file = Request::input("file");
|
||||
$path = base_path("electron/dist/" . $file);
|
||||
if (!file_exists($path)) {
|
||||
return Base::ajaxError("No file");
|
||||
}
|
||||
return Response::download($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/system/imgupload 10. 上传图片
|
||||
*
|
||||
@ -296,7 +315,7 @@ class SystemController extends AbstractController
|
||||
if (!$scale[0] && !$scale[1]) {
|
||||
$scale = [2160, 4160, -1];
|
||||
}
|
||||
$path = "uploads/picture/" . User::userid() . "/" . date("Ym") . "/";
|
||||
$path = "uploads/user/picture/" . User::userid() . "/" . date("Ym") . "/";
|
||||
$image64 = trim(Base::getPostValue('image64'));
|
||||
$fileName = trim(Base::getPostValue('filename'));
|
||||
if ($image64) {
|
||||
@ -341,7 +360,7 @@ class SystemController extends AbstractController
|
||||
if (User::userid() === 0) {
|
||||
return Base::retError('身份失效,等重新登录');
|
||||
}
|
||||
$publicPath = "uploads/picture/" . User::userid() . "/";
|
||||
$publicPath = "uploads/user/picture/" . User::userid() . "/";
|
||||
$dirPath = public_path($publicPath);
|
||||
$dirs = $files = [];
|
||||
//
|
||||
@ -439,7 +458,7 @@ class SystemController extends AbstractController
|
||||
if (User::userid() === 0) {
|
||||
return Base::retError('身份失效,等重新登录');
|
||||
}
|
||||
$path = "uploads/files/" . User::userid() . "/" . date("Ym") . "/";
|
||||
$path = "uploads/user/file/" . User::userid() . "/" . date("Ym") . "/";
|
||||
$image64 = trim(Base::getPostValue('image64'));
|
||||
$fileName = trim(Base::getPostValue('filename'));
|
||||
if ($image64) {
|
||||
|
@ -31,7 +31,7 @@ class UsersController extends AbstractController
|
||||
* @apiParam {String} email 邮箱
|
||||
* @apiParam {String} password 密码
|
||||
* @apiParam {String} [code] 登录验证码
|
||||
* @apiParam {String} [key] 登陆验证码key
|
||||
* @apiParam {String} [invite] 注册邀请码
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@ -46,24 +46,22 @@ class UsersController extends AbstractController
|
||||
$setting = Base::setting('system');
|
||||
if ($setting['reg'] == 'close') {
|
||||
return Base::retError('未开放注册');
|
||||
} elseif ($setting['reg'] == 'invite') {
|
||||
$invite = trim(Request::input('invite'));
|
||||
if (empty($invite) || $invite != $setting['reg_invite']) {
|
||||
return Base::retError('请输入正确的邀请码');
|
||||
}
|
||||
}
|
||||
$user = User::reg($email, $password);
|
||||
} else {
|
||||
$needCode = !Base::isError(User::needCode($email));
|
||||
if ($needCode) {
|
||||
$code = trim(Request::input('code'));
|
||||
$key = trim(Request::input('key'));
|
||||
if (empty($code)) {
|
||||
return Base::retError('请输入验证码', ['code' => 'need']);
|
||||
}
|
||||
if (empty($key)) {
|
||||
if (!Captcha::check($code)) {
|
||||
return Base::retError('请输入正确的验证码', ['code' => 'need']);
|
||||
}
|
||||
} else {
|
||||
if (!Captcha::check_api($code, $key)) {
|
||||
return Base::retError('请输入正确的验证码', ['code' => 'need']);
|
||||
}
|
||||
if (!Captcha::check($code)) {
|
||||
return Base::retError('请输入正确的验证码', ['code' => 'need']);
|
||||
}
|
||||
}
|
||||
//
|
||||
@ -155,7 +153,26 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/info 05. 获取我的信息
|
||||
* @api {get} api/users/reg/needinvite 05. 是否需要邀请码
|
||||
*
|
||||
* @apiDescription 用于判断注册是否需要邀请码
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName reg__needinvite
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function reg__needinvite()
|
||||
{
|
||||
return Base::retSuccess('success', [
|
||||
'need' => Base::settingFind('system', 'reg') == 'invite'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/info 06. 获取我的信息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@ -191,7 +208,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/editdata 06. 修改自己的资料
|
||||
* @api {get} api/users/editdata 07. 修改自己的资料
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@ -210,6 +227,7 @@ class UsersController extends AbstractController
|
||||
{
|
||||
$user = User::auth();
|
||||
$data = Request::all();
|
||||
$user->checkSystem(1);
|
||||
//头像
|
||||
if (Arr::exists($data, 'userimg')) {
|
||||
$userimg = Request::input('userimg');
|
||||
@ -217,13 +235,13 @@ class UsersController extends AbstractController
|
||||
$userimg = is_array($userimg) ? $userimg[0]['path'] : $userimg;
|
||||
$user->userimg = Base::unFillUrl($userimg);
|
||||
} else {
|
||||
$user->userimg = '';
|
||||
$user->userimg = $user->getUserimgAttribute(null);
|
||||
}
|
||||
}
|
||||
//昵称
|
||||
if (Arr::exists($data, 'nickname')) {
|
||||
$nickname = trim(Request::input('nickname'));
|
||||
if (mb_strlen($nickname) < 2) {
|
||||
if ($nickname && mb_strlen($nickname) < 2) {
|
||||
return Base::retError('昵称不可以少于2个字');
|
||||
} elseif (mb_strlen($nickname) > 20) {
|
||||
return Base::retError('昵称最多只能设置20个字');
|
||||
@ -234,7 +252,7 @@ class UsersController extends AbstractController
|
||||
//职位/职称
|
||||
if (Arr::exists($data, 'profession')) {
|
||||
$profession = trim(Request::input('profession'));
|
||||
if (mb_strlen($profession) < 2) {
|
||||
if ($profession && mb_strlen($profession) < 2) {
|
||||
return Base::retError('职位/职称不可以少于2个字');
|
||||
} elseif (mb_strlen($profession) > 20) {
|
||||
return Base::retError('职位/职称最多只能设置20个字');
|
||||
@ -250,7 +268,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/editpass 07. 修改自己的密码
|
||||
* @api {get} api/users/editpass 08. 修改自己的密码
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@ -267,26 +285,14 @@ class UsersController extends AbstractController
|
||||
public function editpass()
|
||||
{
|
||||
$user = User::auth();
|
||||
$user->checkSystem();
|
||||
//
|
||||
$oldpass = trim(Request::input('oldpass'));
|
||||
$newpass = trim(Request::input('newpass'));
|
||||
if (strlen($newpass) < 6) {
|
||||
return Base::retError('密码设置不能小于6位数');
|
||||
} elseif (strlen($newpass) > 32) {
|
||||
return Base::retError('密码最多只能设置32位数');
|
||||
}
|
||||
if ($oldpass == $newpass) {
|
||||
return Base::retError('新旧密码一致');
|
||||
}
|
||||
//
|
||||
if (env("PASSWORD_ADMIN") == 'disabled') {
|
||||
if ($user->userid == 1) {
|
||||
return Base::retError('当前环境禁止修改密码');
|
||||
}
|
||||
}
|
||||
if (env("PASSWORD_OWNER") == 'disabled') {
|
||||
return Base::retError('当前环境禁止修改密码');
|
||||
}
|
||||
User::passwordPolicy($newpass);
|
||||
//
|
||||
$verify = User::whereUserid($user->userid)->wherePassword(Base::md52($oldpass, User::token2encrypt()))->count();
|
||||
if (empty($verify)) {
|
||||
@ -302,7 +308,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/search 08. 搜索会员列表
|
||||
* @api {get} api/users/search 09. 搜索会员列表
|
||||
*
|
||||
* @apiDescription 搜索会员列表
|
||||
* @apiVersion 1.0.0
|
||||
@ -310,11 +316,12 @@ class UsersController extends AbstractController
|
||||
* @apiName searchinfo
|
||||
*
|
||||
* @apiParam {Object} keys 搜索条件
|
||||
* - keys.key 昵称、邮箱
|
||||
* - keys.key 昵称、邮箱关键字
|
||||
* - keys.disable 0-排除禁止(默认),1-含禁止,2-仅禁止
|
||||
* - keys.project_id 在指定项目ID
|
||||
* - keys.no_project_id 不在指定项目ID
|
||||
* @apiParam {Object} sorts 排序方式
|
||||
* - sorts.az 字母
|
||||
* - sorts.az 按字母:asc|desc
|
||||
*
|
||||
* @apiParam {Number} [take] 获取数量,10-100
|
||||
* @apiParam {Number} [page] 当前页,默认:1(赋值分页模式,take参数无效)
|
||||
@ -330,28 +337,32 @@ class UsersController extends AbstractController
|
||||
//
|
||||
$keys = Request::input('keys');
|
||||
$sorts = Request::input('sorts');
|
||||
if (is_array($keys)) {
|
||||
if ($keys['key']) {
|
||||
$builder->where(function($query) use ($keys) {
|
||||
$query->where("email", "like", "%{$keys['key']}%")
|
||||
->orWhere("nickname", "like", "%{$keys['key']}%");
|
||||
});
|
||||
}
|
||||
if (intval($keys['project_id']) > 0) {
|
||||
$builder->whereIn('userid', function ($query) use ($keys) {
|
||||
$query->select('userid')->from('project_users')->where('project_id', $keys['project_id']);
|
||||
});
|
||||
}
|
||||
if (intval($keys['no_project_id']) > 0) {
|
||||
$builder->whereNotIn('userid', function ($query) use ($keys) {
|
||||
$query->select('userid')->from('project_users')->where('project_id', $keys['no_project_id']);
|
||||
});
|
||||
}
|
||||
$keys = is_array($keys) ? $keys : [];
|
||||
$sorts = is_array($sorts) ? $sorts : [];
|
||||
//
|
||||
if ($keys['key']) {
|
||||
$builder->where(function($query) use ($keys) {
|
||||
$query->where("email", "like", "%{$keys['key']}%")
|
||||
->orWhere("nickname", "like", "%{$keys['key']}%");
|
||||
});
|
||||
}
|
||||
if (is_array($sorts)) {
|
||||
if (in_array($sorts['az'], ['asc', 'desc'])) {
|
||||
$builder->orderBy('az', $sorts['az']);
|
||||
}
|
||||
if (intval($keys['disable']) == 0) {
|
||||
$builder->whereNull("disable_at");
|
||||
} elseif (intval($keys['disable']) == 2) {
|
||||
$builder->whereNotNull("disable_at");
|
||||
}
|
||||
if (intval($keys['project_id']) > 0) {
|
||||
$builder->whereIn('userid', function ($query) use ($keys) {
|
||||
$query->select('userid')->from('project_users')->where('project_id', $keys['project_id']);
|
||||
});
|
||||
}
|
||||
if (intval($keys['no_project_id']) > 0) {
|
||||
$builder->whereNotIn('userid', function ($query) use ($keys) {
|
||||
$query->select('userid')->from('project_users')->where('project_id', $keys['no_project_id']);
|
||||
});
|
||||
}
|
||||
if (in_array($sorts['az'], ['asc', 'desc'])) {
|
||||
$builder->orderBy('az', $sorts['az']);
|
||||
}
|
||||
//
|
||||
if (Request::exists('page')) {
|
||||
@ -363,14 +374,14 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/basic 09. 获取指定会员基础信息
|
||||
* @api {get} api/users/basic 10. 获取指定会员基础信息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName basic
|
||||
*
|
||||
* @apiParam {Number} userid 会员ID(多个格式:jsonArray,一次最多30个)
|
||||
* @apiParam {Number} userid 会员ID(多个格式:jsonArray,一次最多50个)
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@ -397,7 +408,12 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员列表(限管理员)
|
||||
* @api {get} api/users/lists 11. 会员列表(限管理员)
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName lists
|
||||
*
|
||||
* @apiParam {Object} [keys] 搜索条件
|
||||
* - keys.email 邮箱
|
||||
@ -405,6 +421,10 @@ class UsersController extends AbstractController
|
||||
* - keys.profession 职位
|
||||
* @apiParam {Number} [page] 当前页,默认:1
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:20,最大:50
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
@ -437,7 +457,12 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作会员(限管理员)
|
||||
* @api {get} api/users/operation 12. 操作会员(限管理员)
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName operation
|
||||
*
|
||||
* @apiParam {Number} userid 会员ID
|
||||
* @apiParam {String} [type] 操作
|
||||
@ -449,6 +474,10 @@ class UsersController extends AbstractController
|
||||
* @apiParam {String} [password] 新的密码
|
||||
* @apiParam {String} [nickname] 昵称
|
||||
* @apiParam {String} [profession] 职位
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function operation()
|
||||
{
|
||||
@ -462,6 +491,7 @@ class UsersController extends AbstractController
|
||||
if (empty($userInfo)) {
|
||||
return Base::retError('会员不存在或已被删除');
|
||||
}
|
||||
$userInfo->checkSystem(1);
|
||||
//
|
||||
$upArray = [];
|
||||
switch ($type) {
|
||||
@ -477,10 +507,12 @@ class UsersController extends AbstractController
|
||||
case 'setdisable':
|
||||
$upArray['identity'] = array_diff($userInfo->identity, ['disable']);
|
||||
$upArray['identity'][] = 'disable';
|
||||
$upArray['disable_at'] = Carbon::now();
|
||||
break;
|
||||
|
||||
case 'cleardisable':
|
||||
$upArray['identity'] = array_diff($userInfo->identity, ['disable']);
|
||||
$upArray['disable_at'] = null;
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
@ -493,11 +525,7 @@ class UsersController extends AbstractController
|
||||
// 密码
|
||||
if (Arr::exists($data, 'password')) {
|
||||
$password = trim($data['password']);
|
||||
if (strlen($password) < 6) {
|
||||
return Base::retError('密码设置不能小于6位数');
|
||||
} elseif (strlen($password) > 32) {
|
||||
return Base::retError('密码最多只能设置32位数');
|
||||
}
|
||||
User::passwordPolicy($password);
|
||||
$upArray['encrypt'] = Base::generatePassword(6);
|
||||
$upArray['password'] = Base::md52($password, $upArray['encrypt']);
|
||||
$upArray['changepass'] = 1;
|
||||
@ -505,7 +533,7 @@ class UsersController extends AbstractController
|
||||
// 昵称
|
||||
if (Arr::exists($data, 'nickname')) {
|
||||
$nickname = trim($data['nickname']);
|
||||
if (mb_strlen($nickname) < 2) {
|
||||
if ($nickname && mb_strlen($nickname) < 2) {
|
||||
return Base::retError('昵称不可以少于2个字');
|
||||
} elseif (mb_strlen($nickname) > 20) {
|
||||
return Base::retError('昵称最多只能设置20个字');
|
||||
@ -516,7 +544,7 @@ class UsersController extends AbstractController
|
||||
// 职位/职称
|
||||
if (Arr::exists($data, 'profession')) {
|
||||
$profession = trim($data['profession']);
|
||||
if (mb_strlen($profession) < 2) {
|
||||
if ($profession && mb_strlen($profession) < 2) {
|
||||
return Base::retError('职位/职称不可以少于2个字');
|
||||
} elseif (mb_strlen($profession) > 20) {
|
||||
return Base::retError('职位/职称最多只能设置20个字');
|
||||
|
@ -4,7 +4,7 @@
|
||||
* 给apidoc项目增加顺序编号
|
||||
*/
|
||||
|
||||
error_reporting(E_ALL & ~E_NOTICE);
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
$path = dirname(__FILE__). '/';
|
||||
$lists = scandir($path);
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Module\Base;
|
||||
use App\Tasks\AutoArchivedTask;
|
||||
use App\Tasks\DeleteTmpTask;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
use Redirect;
|
||||
@ -58,6 +59,8 @@ class IndexController extends InvokeController
|
||||
// 删除过期的临时表数据
|
||||
Task::deliver(new DeleteTmpTask('wg_tmp_msgs', 1));
|
||||
Task::deliver(new DeleteTmpTask('tmp', 24));
|
||||
// 自动归档任务
|
||||
Task::deliver(new AutoArchivedTask());
|
||||
|
||||
return "success";
|
||||
}
|
||||
|
@ -21,14 +21,20 @@ class VerifyCsrfToken extends Middleware
|
||||
// 保存任务优先级
|
||||
'api/system/priority/',
|
||||
|
||||
// 保存创建项目列表模板
|
||||
'api/system/column/template/',
|
||||
|
||||
// 添加任务
|
||||
'api/project/task/add/',
|
||||
|
||||
// 保存工作流
|
||||
'api/project/flow/save/',
|
||||
|
||||
// 修改任务
|
||||
'api/project/task/update/',
|
||||
|
||||
// 上传任务问题
|
||||
'api/project/task/upload/',
|
||||
// 聊天发文本
|
||||
'api/dialog/msg/sendtext/',
|
||||
|
||||
// 聊天发文件
|
||||
'api/dialog/msg/sendfile/',
|
||||
@ -41,5 +47,8 @@ class VerifyCsrfToken extends Middleware
|
||||
|
||||
// 保存文件内容(上传)
|
||||
'api/file/content/upload/',
|
||||
|
||||
// 保存汇报
|
||||
'api/report/store/',
|
||||
];
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ use Illuminate\Support\Facades\DB;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
|
||||
* @method static \Illuminate\Database\Eloquent\Model|object|static|null cancelAppend()
|
||||
* @method static \Illuminate\Database\Eloquent\Model|object|static|null cancelHidden()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|static with($relations)
|
||||
* @method static \Illuminate\Database\Query\Builder|static select($columns = [])
|
||||
* @method static \Illuminate\Database\Query\Builder|static whereNotIn($column, $values, $boolean = 'and')
|
||||
@ -62,6 +64,24 @@ class AbstractModel extends Model
|
||||
return $this->$key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消附加值
|
||||
* @return static
|
||||
*/
|
||||
protected function scopeCancelAppend()
|
||||
{
|
||||
return $this->setAppends([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消隐藏值
|
||||
* @return static
|
||||
*/
|
||||
protected function scopeCancelHidden()
|
||||
{
|
||||
return $this->setHidden([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为数组 / JSON 序列化准备日期。
|
||||
* @param DateTimeInterface $date
|
||||
@ -133,16 +153,23 @@ class AbstractModel extends Model
|
||||
* @param $where
|
||||
* @param array $update 存在时更新的内容
|
||||
* @param array $insert 不存在时插入的内容,如果没有则插入更新内容
|
||||
* @param bool $isInsert 是否是插入数据
|
||||
* @return AbstractModel|\Illuminate\Database\Eloquent\Builder|Model|object|static|null
|
||||
*/
|
||||
public static function updateInsert($where, $update = [], $insert = [])
|
||||
public static function updateInsert($where, $update = [], $insert = [], &$isInsert = true)
|
||||
{
|
||||
$row = static::where($where)->first();
|
||||
if (empty($row)) {
|
||||
$row = new static;
|
||||
$row->updateInstance(array_merge($where, $insert ?: $update));
|
||||
$array = array_merge($where, $insert ?: $update);
|
||||
if (isset($array[$row->primaryKey])) {
|
||||
unset($array[$row->primaryKey]);
|
||||
}
|
||||
$row->updateInstance($array);
|
||||
$isInsert = true;
|
||||
} elseif ($update) {
|
||||
$row->updateInstance($update);
|
||||
$isInsert = false;
|
||||
}
|
||||
if (!$row->save()) {
|
||||
return null;
|
||||
|
@ -10,18 +10,18 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Request;
|
||||
|
||||
/**
|
||||
* Class File
|
||||
* App\Models\File
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $pid 上级ID
|
||||
* @property int|null $cid 复制ID
|
||||
* @property string|null $name 名称
|
||||
* @property string|null $type 类型
|
||||
* @property string|null $ext 后缀名
|
||||
* @property int|null $size 大小(B)
|
||||
* @property int|null $userid 拥有者ID
|
||||
* @property int|null $share 是否共享(1:共享所有人,2:指定成员)
|
||||
* @property int|null $created_id 创建者ID
|
||||
* @property int|null $share 是否共享
|
||||
* @property int|null $created_id 创建者
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
@ -33,6 +33,7 @@ use Request;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereCreatedId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereDeletedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereExt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File wherePid($value)
|
||||
@ -50,43 +51,61 @@ class File extends AbstractModel
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* 是否有访问权限(没有时抛出异常)
|
||||
* @param $userid
|
||||
* 文件文件
|
||||
*/
|
||||
public function exceAllow($userid)
|
||||
{
|
||||
if (!$this->chackAllow($userid)) {
|
||||
throw new ApiException('没有访问权限');
|
||||
}
|
||||
}
|
||||
const codeExt = [
|
||||
'txt',
|
||||
'htaccess', 'htgroups', 'htpasswd', 'conf', 'bat', 'cmd', 'cpp', 'c', 'cc', 'cxx', 'h', 'hh', 'hpp', 'ino', 'cs', 'css',
|
||||
'dockerfile', 'go', 'html', 'htm', 'xhtml', 'vue', 'we', 'wpy', 'java', 'js', 'jsm', 'jsx', 'json', 'jsp', 'less', 'lua', 'makefile', 'gnumakefile',
|
||||
'ocamlmakefile', 'make', 'mysql', 'nginx', 'ini', 'cfg', 'prefs', 'm', 'mm', 'pl', 'pm', 'p6', 'pl6', 'pm6', 'pgsql', 'php',
|
||||
'inc', 'phtml', 'shtml', 'php3', 'php4', 'php5', 'phps', 'phpt', 'aw', 'ctp', 'module', 'ps1', 'py', 'r', 'rb', 'ru', 'gemspec', 'rake', 'guardfile', 'rakefile',
|
||||
'gemfile', 'rs', 'sass', 'scss', 'sh', 'bash', 'bashrc', 'sql', 'sqlserver', 'swift', 'ts', 'typescript', 'str', 'vbs', 'vb', 'v', 'vh', 'sv', 'svh', 'xml',
|
||||
'rdf', 'rss', 'wsdl', 'xslt', 'atom', 'mathml', 'mml', 'xul', 'xbl', 'xaml', 'yaml', 'yml',
|
||||
'asp', 'properties', 'gitignore', 'log', 'bas', 'prg', 'python', 'ftl', 'aspx'
|
||||
];
|
||||
|
||||
/**
|
||||
* office文件
|
||||
*/
|
||||
const officeExt = [
|
||||
'doc', 'docx',
|
||||
'xls', 'xlsx',
|
||||
'ppt', 'pptx',
|
||||
];
|
||||
|
||||
/**
|
||||
* 本地媒体文件
|
||||
*/
|
||||
const localExt = [
|
||||
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'ico', 'raw',
|
||||
'tif', 'tiff',
|
||||
'mp3', 'wav', 'mp4', 'flv',
|
||||
'avi', 'mov', 'wmv', 'mkv', '3gp', 'rm',
|
||||
];
|
||||
|
||||
/**
|
||||
* 是否有访问权限
|
||||
* ① 自己的文件夹
|
||||
* ② 共享所有人的文件夹
|
||||
* ③ 在指定共享人员内
|
||||
* @param $userid
|
||||
* @return bool
|
||||
* @return int -1:没有权限,0:访问权限,1:读写权限,1000:所有者或创建者
|
||||
*/
|
||||
public function chackAllow($userid)
|
||||
public function getPermission($userid)
|
||||
{
|
||||
if ($userid == $this->userid) {
|
||||
// ① 自己的文件夹
|
||||
return true;
|
||||
if ($userid == $this->userid || $userid == $this->created_id) {
|
||||
// ① 自己的文件夹 或 自己创建的文件夹
|
||||
return 1000;
|
||||
}
|
||||
$row = $this->getShareInfo();
|
||||
if ($row) {
|
||||
if ($row->share == 1) {
|
||||
// ② 共享所有人的文件夹
|
||||
return true;
|
||||
} elseif ($row->share == 2) {
|
||||
// ③ 在指定共享人员内
|
||||
if (FileUser::whereFileId($row->id)->whereUserid($userid)->exists()) {
|
||||
return true;
|
||||
}
|
||||
$fileUser = FileUser::whereFileId($row->id)->where(function ($query) use ($userid) {
|
||||
$query->where('userid', 0);
|
||||
$query->orWhere('userid', $userid);
|
||||
})->orderByDesc('permission')->first();
|
||||
if ($fileUser) {
|
||||
// ② 在指定共享成员内
|
||||
return $fileUser->permission;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,7 +114,7 @@ class File extends AbstractModel
|
||||
*/
|
||||
public function getShareInfo()
|
||||
{
|
||||
if ($this->share > 0) {
|
||||
if ($this->share) {
|
||||
return $this;
|
||||
}
|
||||
$pid = $this->pid;
|
||||
@ -104,7 +123,7 @@ class File extends AbstractModel
|
||||
if (empty($row)) {
|
||||
break;
|
||||
}
|
||||
if ($row->share > 0) {
|
||||
if ($row->share) {
|
||||
return $row;
|
||||
}
|
||||
$pid = $row->pid;
|
||||
@ -124,7 +143,7 @@ class File extends AbstractModel
|
||||
if (empty($row)) {
|
||||
break;
|
||||
}
|
||||
if ($row->share > 0) {
|
||||
if ($row->share) {
|
||||
return true;
|
||||
}
|
||||
$pid = $row->pid;
|
||||
@ -137,18 +156,23 @@ class File extends AbstractModel
|
||||
* @param $share
|
||||
* @return bool
|
||||
*/
|
||||
public function setShare($share)
|
||||
public function setShare($share = null)
|
||||
{
|
||||
AbstractModel::transaction(function () use ($share) {
|
||||
$this->share = $share;
|
||||
$this->save();
|
||||
$list = self::wherePid($this->id)->get();
|
||||
if ($list->isNotEmpty()) {
|
||||
foreach ($list as $item) {
|
||||
$item->setShare(0);
|
||||
if ($share === null) {
|
||||
$share = FileUser::whereFileId($this->id)->count() == 0 ? 0 : 1;
|
||||
}
|
||||
if ($this->share != $share) {
|
||||
AbstractModel::transaction(function () use ($share) {
|
||||
$this->share = $share;
|
||||
$this->save();
|
||||
$list = self::wherePid($this->id)->get();
|
||||
if ($list->isNotEmpty()) {
|
||||
foreach ($list as $item) {
|
||||
$item->setShare(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -161,6 +185,8 @@ class File extends AbstractModel
|
||||
AbstractModel::transaction(function () {
|
||||
$this->delete();
|
||||
$this->pushMsg('delete');
|
||||
FileLink::whereFileId($this->id)->delete();
|
||||
FileUser::whereFileId($this->id)->delete();
|
||||
FileContent::whereFid($this->id)->delete();
|
||||
$list = self::wherePid($this->id)->get();
|
||||
if ($list->isNotEmpty()) {
|
||||
@ -224,16 +250,99 @@ class File extends AbstractModel
|
||||
/**
|
||||
* 获取文件并检测权限
|
||||
* @param $id
|
||||
* @param null $noExistTis
|
||||
* @param int $limit 要求权限: 0-访问权限、1-读写权限、1000-所有者或创建者
|
||||
* @param $permission
|
||||
* @return File
|
||||
*/
|
||||
public static function allowFind($id, $noExistTis = null)
|
||||
public static function permissionFind($id, $limit = 0, &$permission = -1)
|
||||
{
|
||||
$file = File::find($id);
|
||||
if (empty($file)) {
|
||||
throw new ApiException($noExistTis ?: '文件不存在或已被删除');
|
||||
throw new ApiException('文件不存在或已被删除');
|
||||
}
|
||||
//
|
||||
$permission = $file->getPermission(User::userid());
|
||||
if ($permission < $limit) {
|
||||
$msg = match ($limit) {
|
||||
1000 => '仅限所有者或创建者操作',
|
||||
1 => '没有读写权限',
|
||||
default => '没有访问权限',
|
||||
};
|
||||
throw new ApiException($msg);
|
||||
}
|
||||
$file->exceAllow(User::userid());
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化内容数据
|
||||
* @param array $data [path, size, ext, name]
|
||||
* @return array
|
||||
*/
|
||||
public static function formatFileData(array $data)
|
||||
{
|
||||
$filePath = $data['path'];
|
||||
$fileSize = $data['size'];
|
||||
$fileExt = $data['ext'];
|
||||
$fileDotExt = '.' . $fileExt;
|
||||
$fileName = Base::rightDelete($data['name'], $fileDotExt) . $fileDotExt;
|
||||
$publicPath = public_path($filePath);
|
||||
//
|
||||
switch ($fileExt) {
|
||||
case 'md':
|
||||
case 'text':
|
||||
// 文本
|
||||
$data['content'] = [
|
||||
'type' => $fileExt,
|
||||
'content' => file_get_contents($publicPath),
|
||||
];
|
||||
$data['file_mode'] = $fileExt;
|
||||
break;
|
||||
|
||||
case 'drawio':
|
||||
// 图表
|
||||
$data['content'] = [
|
||||
'xml' => file_get_contents($publicPath)
|
||||
];
|
||||
$data['file_mode'] = $fileExt;
|
||||
break;
|
||||
|
||||
case 'mind':
|
||||
// 思维导图
|
||||
$data['content'] = Base::json2array(file_get_contents($publicPath));
|
||||
$data['file_mode'] = $fileExt;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (in_array($fileExt, self::codeExt) && $fileSize < 2 * 1024 * 1024)
|
||||
{
|
||||
// 文本预览,限制2M内的文件
|
||||
$data['content'] = file_get_contents($publicPath);
|
||||
$data['file_mode'] = 'code';
|
||||
}
|
||||
elseif (in_array($fileExt, File::officeExt))
|
||||
{
|
||||
// office预览
|
||||
$data['content'] = '';
|
||||
$data['file_mode'] = 'office';
|
||||
}
|
||||
else
|
||||
{
|
||||
// 其他预览
|
||||
if (in_array($fileExt, File::localExt)) {
|
||||
$url = Base::fillUrl($filePath);
|
||||
} else {
|
||||
$url = 'http://' . env('APP_IPPR') . '.3/' . $filePath;
|
||||
}
|
||||
$data['content'] = [
|
||||
'preview' => true,
|
||||
'url' => base64_encode(Base::urlAddparameter($url, [
|
||||
'fullfilename' => $fileName
|
||||
])),
|
||||
];
|
||||
$data['file_mode'] = 'preview';
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
* Class FileContent
|
||||
* App\Models\FileContent
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $fid 文件ID
|
||||
* @property string|null $content 内容
|
||||
@ -42,41 +41,53 @@ class FileContent extends AbstractModel
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* 获取格式内容
|
||||
* @param $type
|
||||
* 获取格式内容(或下载)
|
||||
* @param File $file
|
||||
* @param $content
|
||||
* @param $download
|
||||
* @return array|\Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
*/
|
||||
public static function formatContent($type, $content)
|
||||
public static function formatContent($file, $content, $download = false)
|
||||
{
|
||||
$content = Base::json2array($content);
|
||||
if (in_array($type, ['word', 'excel', 'ppt'])) {
|
||||
$name = $file->ext ? "{$file->name}.{$file->ext}" : null;
|
||||
$content = Base::json2array($content ?: []);
|
||||
if (in_array($file->type, ['word', 'excel', 'ppt'])) {
|
||||
if (empty($content)) {
|
||||
return Response::download(resource_path('assets/statics/office/empty.' . str_replace(['word', 'excel', 'ppt'], ['docx', 'xlsx', 'pptx'], $type)));
|
||||
return Response::download(resource_path('assets/statics/office/empty.' . str_replace(['word', 'excel', 'ppt'], ['docx', 'xlsx', 'pptx'], $file->type)), $name);
|
||||
}
|
||||
return Response::download(public_path($content['url']));
|
||||
return Response::download(public_path($content['url']), $name);
|
||||
}
|
||||
if (empty($content)) {
|
||||
switch ($type) {
|
||||
case 'document':
|
||||
$content = [
|
||||
"type" => "md",
|
||||
"content" => "",
|
||||
];
|
||||
break;
|
||||
|
||||
case 'sheet':
|
||||
$content = [
|
||||
[
|
||||
"name" => "Sheet1",
|
||||
"config" => json_decode('{}'),
|
||||
]
|
||||
];
|
||||
break;
|
||||
|
||||
default:
|
||||
$content = json_decode('{}');
|
||||
break;
|
||||
$content = match ($file->type) {
|
||||
'document' => [
|
||||
"type" => $file->ext,
|
||||
"content" => "",
|
||||
],
|
||||
default => json_decode('{}'),
|
||||
};
|
||||
if ($download) {
|
||||
abort(403, "This file is empty.");
|
||||
}
|
||||
} else {
|
||||
$path = $content['url'];
|
||||
if ($file->ext) {
|
||||
$res = File::formatFileData([
|
||||
'path' => $path,
|
||||
'ext' => $file->ext,
|
||||
'size' => $file->size,
|
||||
'name' => $file->name,
|
||||
]);
|
||||
$content = $res['content'];
|
||||
} else {
|
||||
$content['preview'] = false;
|
||||
}
|
||||
if ($download) {
|
||||
$filePath = public_path($path);
|
||||
if (isset($filePath)) {
|
||||
return Response::download($filePath, $name);
|
||||
} else {
|
||||
abort(403, "This file not support download.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return Base::retSuccess('success', [ 'content' => $content ]);
|
||||
|
35
app/Models/FileLink.php
Normal file
35
app/Models/FileLink.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* App\Models\FileLink
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $file_id 项目ID
|
||||
* @property int|null $num 累计访问
|
||||
* @property string|null $code 链接码
|
||||
* @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|FileLink newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileLink newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileLink query()
|
||||
* @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)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileLink whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileLink whereNum($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileLink whereUpdatedAt($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class FileLink extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function file(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(File::class, 'id', 'file_id');
|
||||
}
|
||||
}
|
@ -4,12 +4,12 @@ namespace App\Models;
|
||||
|
||||
|
||||
/**
|
||||
* Class FileUser
|
||||
* App\Models\FileUser
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $file_id 项目ID
|
||||
* @property int|null $userid 成员ID
|
||||
* @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|FileUser newModelQuery()
|
||||
@ -18,6 +18,7 @@ namespace App\Models;
|
||||
* @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)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileUser wherePermission($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileUser whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileUser whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
|
@ -3,40 +3,36 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Module\Base;
|
||||
use App\Tasks\PushTask;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Request;
|
||||
|
||||
/**
|
||||
* Class Project
|
||||
* App\Models\Project
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property string|null $name 名称
|
||||
* @property string|null $desc 描述、备注
|
||||
* @property int|null $userid 创建人
|
||||
* @property int|mixed $dialog_id 聊天会话ID
|
||||
* @property int|null $dialog_id 聊天会话ID
|
||||
* @property string|null $archived_at 归档时间
|
||||
* @property int|null $archived_userid 归档会员
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @property-read int $owner_userid
|
||||
* @property-read int $task_complete
|
||||
* @property-read int $task_my_complete
|
||||
* @property-read int $task_my_num
|
||||
* @property-read int $task_my_percent
|
||||
* @property-read int $task_num
|
||||
* @property-read int $task_percent
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectColumn[] $projectColumn
|
||||
* @property-read int|null $project_column_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectLog[] $projectLog
|
||||
* @property-read int|null $project_log_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectUser[] $projectUser
|
||||
* @property-read int|null $project_user_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project authData($userid = null)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project allData($userid = null)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project authData($userid = null, $owner = null)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|Project onlyTrashed()
|
||||
@ -59,99 +55,14 @@ class Project extends AbstractModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
const projectSelect = [
|
||||
'projects.*',
|
||||
'project_users.owner',
|
||||
protected $hidden = [
|
||||
'deleted_at',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'task_num',
|
||||
'task_complete',
|
||||
'task_percent',
|
||||
'task_my_num',
|
||||
'task_my_complete',
|
||||
'task_my_percent',
|
||||
'owner_userid',
|
||||
];
|
||||
|
||||
/**
|
||||
* 生成任务数据
|
||||
*/
|
||||
private function generateTaskData()
|
||||
{
|
||||
if (!isset($this->appendattrs['task_num'])) {
|
||||
$builder = ProjectTask::whereProjectId($this->id)->whereParentId(0)->whereNull('archived_at');
|
||||
$this->appendattrs['task_num'] = $builder->count();
|
||||
$this->appendattrs['task_complete'] = $builder->whereNotNull('complete_at')->count();
|
||||
$this->appendattrs['task_percent'] = $this->appendattrs['task_num'] ? intval($this->appendattrs['task_complete'] / $this->appendattrs['task_num'] * 100) : 0;
|
||||
//
|
||||
$builder = ProjectTask::whereProjectId($this->id)->whereParentId(0)->authData(User::userid())->whereNull('archived_at');
|
||||
$this->appendattrs['task_my_num'] = $builder->count();
|
||||
$this->appendattrs['task_my_complete'] = $builder->whereNotNull('complete_at')->count();
|
||||
$this->appendattrs['task_my_percent'] = $this->appendattrs['task_my_num'] ? intval($this->appendattrs['task_my_complete'] / $this->appendattrs['task_my_num'] * 100) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务数量
|
||||
* @return int
|
||||
*/
|
||||
public function getTaskNumAttribute()
|
||||
{
|
||||
$this->generateTaskData();
|
||||
return $this->appendattrs['task_num'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务完成数量
|
||||
* @return int
|
||||
*/
|
||||
public function getTaskCompleteAttribute()
|
||||
{
|
||||
$this->generateTaskData();
|
||||
return $this->appendattrs['task_complete'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务完成率
|
||||
* @return int
|
||||
*/
|
||||
public function getTaskPercentAttribute()
|
||||
{
|
||||
$this->generateTaskData();
|
||||
return $this->appendattrs['task_percent'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务数量(我的)
|
||||
* @return int
|
||||
*/
|
||||
public function getTaskMyNumAttribute()
|
||||
{
|
||||
$this->generateTaskData();
|
||||
return $this->appendattrs['task_my_num'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务完成数量(我的)
|
||||
* @return int
|
||||
*/
|
||||
public function getTaskMyCompleteAttribute()
|
||||
{
|
||||
$this->generateTaskData();
|
||||
return $this->appendattrs['task_my_complete'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务完成率(我的)
|
||||
* @return int
|
||||
*/
|
||||
public function getTaskMyPercentAttribute()
|
||||
{
|
||||
$this->generateTaskData();
|
||||
return $this->appendattrs['task_my_percent'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 负责人会员ID
|
||||
* @return int
|
||||
@ -159,7 +70,7 @@ class Project extends AbstractModel
|
||||
public function getOwnerUseridAttribute()
|
||||
{
|
||||
if (!isset($this->appendattrs['owner_userid'])) {
|
||||
$ownerUser = $this->projectUser->where('owner', 1)->first();
|
||||
$ownerUser = ProjectUser::whereProjectId($this->id)->whereOwner(1)->first();
|
||||
$this->appendattrs['owner_userid'] = $ownerUser ? $ownerUser->userid : 0;
|
||||
}
|
||||
return $this->appendattrs['owner_userid'];
|
||||
@ -190,19 +101,73 @@ class Project extends AbstractModel
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询自己的项目
|
||||
* 查询所有项目(与正常查询多返回owner字段)
|
||||
* @param self $query
|
||||
* @param null $userid
|
||||
* @return self
|
||||
*/
|
||||
public function scopeAuthData($query, $userid = null)
|
||||
public function scopeAllData($query, $userid = null)
|
||||
{
|
||||
$userid = $userid ?: User::userid();
|
||||
$query->join('project_users', 'projects.id', '=', 'project_users.project_id')
|
||||
->where('project_users.userid', $userid);
|
||||
$query
|
||||
->select([
|
||||
'projects.*',
|
||||
'project_users.owner',
|
||||
'project_users.top_at',
|
||||
])
|
||||
->leftJoin('project_users', function ($leftJoin) use ($userid) {
|
||||
$leftJoin
|
||||
->on('project_users.userid', '=', DB::raw($userid))
|
||||
->on('projects.id', '=', 'project_users.project_id');
|
||||
});
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询自己负责或参与的项目
|
||||
* @param self $query
|
||||
* @param null $userid
|
||||
* @param null $owner
|
||||
* @return self
|
||||
*/
|
||||
public function scopeAuthData($query, $userid = null, $owner = null)
|
||||
{
|
||||
$userid = $userid ?: User::userid();
|
||||
$query
|
||||
->select([
|
||||
'projects.*',
|
||||
'project_users.owner',
|
||||
'project_users.top_at',
|
||||
])
|
||||
->join('project_users', 'projects.id', '=', 'project_users.project_id')
|
||||
->where('project_users.userid', $userid);
|
||||
if ($owner !== null) {
|
||||
$query->where('project_users.owner', $owner);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务统计数据
|
||||
* @param $userid
|
||||
* @return array
|
||||
*/
|
||||
public function getTaskStatistics($userid)
|
||||
{
|
||||
$array = [];
|
||||
$builder = ProjectTask::whereProjectId($this->id)->whereNull('archived_at');
|
||||
$array['task_num'] = $builder->count();
|
||||
$array['task_complete'] = $builder->whereNotNull('complete_at')->count();
|
||||
$array['task_percent'] = $array['task_num'] ? intval($array['task_complete'] / $array['task_num'] * 100) : 0;
|
||||
//
|
||||
$builder = ProjectTask::authData($userid, 1)->where('project_tasks.project_id', $this->id)->whereNull('project_tasks.archived_at');
|
||||
$array['task_my_num'] = $builder->count();
|
||||
$array['task_my_complete'] = $builder->whereNotNull('project_tasks.complete_at')->count();
|
||||
$array['task_my_percent'] = $array['task_my_num'] ? intval($array['task_my_complete'] / $array['task_my_num'] * 100) : 0;
|
||||
//
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入项目
|
||||
* @param int $userid 加入的会员ID
|
||||
@ -249,7 +214,7 @@ class Project extends AbstractModel
|
||||
*/
|
||||
public function relationUserids()
|
||||
{
|
||||
return $this->projectUser->pluck('userid')->toArray();
|
||||
return ProjectUser::whereProjectId($this->id)->orderBy('id')->pluck('userid')->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -267,7 +232,7 @@ class Project extends AbstractModel
|
||||
}
|
||||
|
||||
/**
|
||||
* 归档任务、取消归档
|
||||
* 归档项目、取消归档
|
||||
* @param Carbon|null $archived_at 归档时间
|
||||
* @return bool
|
||||
*/
|
||||
@ -277,14 +242,23 @@ class Project extends AbstractModel
|
||||
if ($archived_at === null) {
|
||||
// 取消归档
|
||||
$this->archived_at = null;
|
||||
$this->archived_userid = User::userid();
|
||||
$this->addLog("项目取消归档");
|
||||
$this->pushMsg('add', $this);
|
||||
ProjectTask::whereProjectId($this->id)->whereArchivedFollow(1)->update([
|
||||
'archived_at' => null,
|
||||
'archived_follow' => 0
|
||||
]);
|
||||
} else {
|
||||
// 归档任务
|
||||
// 归档项目
|
||||
$this->archived_at = $archived_at;
|
||||
$this->archived_userid = User::userid();
|
||||
$this->addLog("项目归档");
|
||||
$this->pushMsg('archived');
|
||||
ProjectTask::whereProjectId($this->id)->whereArchivedAt(null)->update([
|
||||
'archived_at' => $archived_at,
|
||||
'archived_follow' => 1
|
||||
]);
|
||||
}
|
||||
$this->save();
|
||||
});
|
||||
@ -299,9 +273,7 @@ class Project extends AbstractModel
|
||||
{
|
||||
AbstractModel::transaction(function () {
|
||||
$dialog = WebSocketDialog::find($this->dialog_id);
|
||||
if ($dialog) {
|
||||
$dialog->deleteDialog();
|
||||
}
|
||||
$dialog?->deleteDialog();
|
||||
$columns = ProjectColumn::whereProjectId($this->id)->get();
|
||||
foreach ($columns as $column) {
|
||||
$column->deleteColumn(false);
|
||||
@ -316,18 +288,23 @@ class Project extends AbstractModel
|
||||
/**
|
||||
* 添加项目日志
|
||||
* @param string $detail
|
||||
* @param array $record
|
||||
* @param int $userid
|
||||
* @return ProjectLog
|
||||
*/
|
||||
public function addLog($detail, $userid = 0)
|
||||
public function addLog($detail, $record = [], $userid = 0)
|
||||
{
|
||||
$log = ProjectLog::createInstance([
|
||||
$array = [
|
||||
'project_id' => $this->id,
|
||||
'column_id' => 0,
|
||||
'task_id' => 0,
|
||||
'userid' => $userid ?: User::userid(),
|
||||
'detail' => $detail,
|
||||
]);
|
||||
];
|
||||
if ($record) {
|
||||
$array['record'] = $record;
|
||||
}
|
||||
$log = ProjectLog::createInstance($array);
|
||||
$log->save();
|
||||
return $log;
|
||||
}
|
||||
@ -335,45 +312,179 @@ class Project extends AbstractModel
|
||||
/**
|
||||
* 推送消息
|
||||
* @param string $action
|
||||
* @param array $data 发送内容,默认为[id=>项目ID]
|
||||
* @param array $userid 指定会员,默认为项目所有成员
|
||||
* @param array|self $data 发送内容,默认为[id=>项目ID]
|
||||
* @param array $userid 指定会员,默认为项目所有成员
|
||||
*/
|
||||
public function pushMsg($action, $data = null, $userid = null)
|
||||
{
|
||||
if ($data === null) {
|
||||
$data = ['id' => $this->id];
|
||||
} elseif ($data instanceof self) {
|
||||
$data = $data->toArray();
|
||||
}
|
||||
//
|
||||
$array = [$userid, []];
|
||||
if ($userid === null) {
|
||||
$userid = $this->relationUserids();
|
||||
$array[0] = $this->relationUserids();
|
||||
} elseif (!is_array($userid)) {
|
||||
$array[0] = [$userid];
|
||||
}
|
||||
//
|
||||
if (isset($data['owner'])) {
|
||||
$owners = ProjectUser::whereProjectId($data['id'])->whereOwner(1)->pluck('userid')->toArray();
|
||||
$array = [array_intersect($array[0], $owners), array_diff($array[0], $owners)];
|
||||
}
|
||||
//
|
||||
foreach ($array as $index => $item) {
|
||||
if ($index > 0) {
|
||||
$data['owner'] = 0;
|
||||
}
|
||||
$params = [
|
||||
'ignoreFd' => Request::header('fd'),
|
||||
'userid' => array_values($item),
|
||||
'msg' => [
|
||||
'type' => 'project',
|
||||
'action' => $action,
|
||||
'data' => $data,
|
||||
]
|
||||
];
|
||||
$task = new PushTask($params, false);
|
||||
Task::deliver($task);
|
||||
}
|
||||
$params = [
|
||||
'ignoreFd' => Request::header('fd'),
|
||||
'userid' => $userid,
|
||||
'msg' => [
|
||||
'type' => 'project',
|
||||
'action' => $action,
|
||||
'data' => $data,
|
||||
]
|
||||
];
|
||||
$task = new PushTask($params, false);
|
||||
Task::deliver($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户获取项目信息(用于判断会员是否存在项目内)
|
||||
* 添加工作流
|
||||
* @param $flows
|
||||
* @return mixed
|
||||
*/
|
||||
public function addFlow($flows)
|
||||
{
|
||||
return AbstractModel::transaction(function() use ($flows) {
|
||||
$projectFlow = ProjectFlow::whereProjectId($this->id)->first();
|
||||
if (empty($projectFlow)) {
|
||||
$projectFlow = ProjectFlow::createInstance([
|
||||
'project_id' => $this->id,
|
||||
'name' => 'Default'
|
||||
]);
|
||||
if (!$projectFlow->save()) {
|
||||
throw new ApiException('工作流创建失败');
|
||||
}
|
||||
}
|
||||
//
|
||||
$ids = [];
|
||||
$idc = [];
|
||||
$hasStart = false;
|
||||
$hasEnd = false;
|
||||
$upTaskList = [];
|
||||
foreach ($flows as $item) {
|
||||
$id = intval($item['id']);
|
||||
$turns = Base::arrayRetainInt($item['turns'] ?: [], true);
|
||||
$userids = Base::arrayRetainInt($item['userids'] ?: [], true);
|
||||
$usertype = trim($item['usertype']);
|
||||
$userlimit = intval($item['userlimit']);
|
||||
if ($usertype == 'replace' && empty($userids)) {
|
||||
throw new ApiException("状态[{$item['name']}]设置错误,设置流转模式时必须填写状态负责人");
|
||||
}
|
||||
if ($usertype == 'merge' && empty($userids)) {
|
||||
throw new ApiException("状态[{$item['name']}]设置错误,设置剔除模式时必须填写状态负责人");
|
||||
}
|
||||
if ($userlimit && empty($userids)) {
|
||||
throw new ApiException("状态[{$item['name']}]设置错误,设置限制负责人时必须填写状态负责人");
|
||||
}
|
||||
$flow = ProjectFlowItem::updateInsert([
|
||||
'id' => $id,
|
||||
'project_id' => $this->id,
|
||||
'flow_id' => $projectFlow->id,
|
||||
], [
|
||||
'name' => trim($item['name']),
|
||||
'status' => trim($item['status']),
|
||||
'sort' => intval($item['sort']),
|
||||
'turns' => $turns,
|
||||
'userids' => $userids,
|
||||
'usertype' => trim($item['usertype']),
|
||||
'userlimit' => $userlimit,
|
||||
], [], $isInsert);
|
||||
if ($flow) {
|
||||
$ids[] = $flow->id;
|
||||
if ($flow->id != $id) {
|
||||
$idc[$id] = $flow->id;
|
||||
}
|
||||
if ($flow->status == 'start') {
|
||||
$hasStart = true;
|
||||
}
|
||||
if ($flow->status == 'end') {
|
||||
$hasEnd = true;
|
||||
}
|
||||
if (!$isInsert) {
|
||||
$upTaskList[$flow->id] = $flow->status . "|" . $flow->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$hasStart) {
|
||||
throw new ApiException('至少需要1个开始状态');
|
||||
}
|
||||
if (!$hasEnd) {
|
||||
throw new ApiException('至少需要1个结束状态');
|
||||
}
|
||||
ProjectFlowItem::whereFlowId($projectFlow->id)->whereNotIn('id', $ids)->chunk(100, function($list) {
|
||||
foreach ($list as $item) {
|
||||
$item->deleteFlowItem();
|
||||
}
|
||||
});
|
||||
//
|
||||
foreach ($upTaskList as $id => $value) {
|
||||
ProjectTask::whereFlowItemId($id)->update([
|
||||
'flow_item_name' => $value
|
||||
]);
|
||||
}
|
||||
//
|
||||
$projectFlow = ProjectFlow::with(['projectFlowItem'])->whereProjectId($this->id)->find($projectFlow->id);
|
||||
$itemIds = $projectFlow->projectFlowItem->pluck('id')->toArray();
|
||||
foreach ($projectFlow->projectFlowItem as $item) {
|
||||
$turns = $item->turns;
|
||||
foreach ($idc as $oid => $nid) {
|
||||
if (in_array($oid, $turns)) {
|
||||
$turns = array_diff($turns, [$oid]);
|
||||
$turns[] = $nid;
|
||||
}
|
||||
}
|
||||
if (!in_array($item->id, $turns)) {
|
||||
$turns[] = $item->id;
|
||||
}
|
||||
$turns = array_values(array_filter(array_unique(array_intersect($turns, $itemIds))));
|
||||
sort($turns);
|
||||
$item->turns = $turns;
|
||||
ProjectFlowItem::whereId($item->id)->update([ 'turns' => Base::array2json($turns) ]);
|
||||
}
|
||||
return $projectFlow;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目信息(用于判断会员是否存在项目内)
|
||||
* @param int $project_id
|
||||
* @param bool $ignoreArchived 排除已归档
|
||||
* @param null|bool $archived true:仅限未归档, false:仅限已归档, null:不限制
|
||||
* @param null|bool $mustOwner true:仅限项目负责人, false:仅限非项目负责人, null:不限制
|
||||
* @return self
|
||||
*/
|
||||
public static function userProject($project_id, $ignoreArchived = true)
|
||||
public static function userProject($project_id, $archived = true, $mustOwner = null)
|
||||
{
|
||||
$builder = self::select(self::projectSelect)->authData()->where('projects.id', intval($project_id));
|
||||
if ($ignoreArchived) {
|
||||
$builder->whereNull('projects.archived_at');
|
||||
}
|
||||
$project = $builder->first();
|
||||
$project = self::authData()->where('projects.id', intval($project_id))->first();
|
||||
if (empty($project)) {
|
||||
throw new ApiException('项目不存在或不在成员列表内');
|
||||
throw new ApiException('项目不存在或不在成员列表内', [ 'project_id' => $project_id ], -4001);
|
||||
}
|
||||
if ($archived === true && $project->archived_at != null) {
|
||||
throw new ApiException('项目已归档', [ 'project_id' => $project_id ], -4001);
|
||||
}
|
||||
if ($archived === false && $project->archived_at == null) {
|
||||
throw new ApiException('项目未归档', [ 'project_id' => $project_id ]);
|
||||
}
|
||||
if ($mustOwner === true && !$project->owner) {
|
||||
throw new ApiException('仅限项目负责人操作', [ 'project_id' => $project_id ]);
|
||||
}
|
||||
if ($mustOwner === false && $project->owner) {
|
||||
throw new ApiException('禁止项目负责人操作', [ 'project_id' => $project_id ]);
|
||||
}
|
||||
return $project;
|
||||
}
|
||||
|
@ -9,9 +9,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Request;
|
||||
|
||||
/**
|
||||
* Class ProjectColumn
|
||||
* App\Models\ProjectColumn
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property string|null $name 列表名称
|
||||
|
51
app/Models/ProjectFlow.php
Normal file
51
app/Models/ProjectFlow.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* App\Models\ProjectFlow
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property string|null $name 流程名称
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectFlowItem[] $projectFlowItem
|
||||
* @property-read int|null $project_flow_item_count
|
||||
* @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|ProjectFlow whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereProjectId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereUpdatedAt($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectFlow extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function projectFlowItem(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
{
|
||||
return $this->hasMany(ProjectFlowItem::class, 'flow_id', 'id')->orderBy('sort');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function deleteFlow()
|
||||
{
|
||||
return AbstractModel::transaction(function() {
|
||||
ProjectFlowItem::whereProjectId($this->project_id)->chunk(100, function($list) {
|
||||
foreach ($list as $item) {
|
||||
$item->deleteFlowItem();
|
||||
}
|
||||
});
|
||||
return $this->delete();
|
||||
});
|
||||
}
|
||||
}
|
90
app/Models/ProjectFlowItem.php
Normal file
90
app/Models/ProjectFlowItem.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* App\Models\ProjectFlowItem
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $flow_id 流程ID
|
||||
* @property string|null $name 名称
|
||||
* @property string|null $status 状态
|
||||
* @property array $turns 可流转
|
||||
* @property array $userids 自动负责人ID
|
||||
* @property string|null $usertype 流转模式
|
||||
* @property int|null $userlimit 限制负责人
|
||||
* @property int|null $sort 排序
|
||||
* @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|ProjectFlowItem newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereFlowId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereProjectId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereSort($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereStatus($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereTurns($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereUserids($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereUserlimit($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereUsertype($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectFlowItem extends AbstractModel
|
||||
{
|
||||
protected $hidden = [
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @return array
|
||||
*/
|
||||
public function getTurnsAttribute($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return Base::json2array($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @return array
|
||||
*/
|
||||
public function getUseridsAttribute($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return Base::json2array($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function projectFlow(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(ProjectFlow::class, 'id', 'flow_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function deleteFlowItem()
|
||||
{
|
||||
ProjectTask::whereFlowItemId($this->id)->update([
|
||||
'flow_item_id' => 0,
|
||||
'flow_item_name' => "",
|
||||
]);
|
||||
return $this->delete();
|
||||
}
|
||||
}
|
55
app/Models/ProjectInvite.php
Normal file
55
app/Models/ProjectInvite.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* App\Models\ProjectInvite
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $num 累计邀请
|
||||
* @property string|null $code 链接码
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read bool $already
|
||||
* @property-read \App\Models\Project|null $project
|
||||
* @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|ProjectInvite whereCode($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite whereNum($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite whereProjectId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectInvite whereUpdatedAt($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectInvite extends AbstractModel
|
||||
{
|
||||
protected $appends = [
|
||||
'already',
|
||||
];
|
||||
|
||||
/**
|
||||
* 是否已加入
|
||||
* @return bool
|
||||
*/
|
||||
public function getAlreadyAttribute()
|
||||
{
|
||||
if (!isset($this->appendattrs['already'])) {
|
||||
$this->appendattrs['already'] = false;
|
||||
if (User::userid()) {
|
||||
$this->appendattrs['already'] = (bool)$this->project?->projectUser?->where('userid', User::userid())->count();
|
||||
}
|
||||
}
|
||||
return $this->appendattrs['already'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function project(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(Project::class, 'id', 'project_id');
|
||||
}
|
||||
}
|
@ -2,18 +2,21 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* Class ProjectLog
|
||||
* App\Models\ProjectLog
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $column_id 列表ID
|
||||
* @property int|null $task_id 项目ID
|
||||
* @property int|null $userid 会员ID
|
||||
* @property string|null $detail 详细信息
|
||||
* @property array $record 记录数据
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @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|ProjectLog newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog newQuery()
|
||||
@ -23,6 +26,7 @@ namespace App\Models;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereDetail($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereId($value)
|
||||
* @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 whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectLog whereUserid($value)
|
||||
@ -31,6 +35,18 @@ namespace App\Models;
|
||||
class ProjectLog extends AbstractModel
|
||||
{
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @return array
|
||||
*/
|
||||
public function getRecordAttribute($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return Base::json2array($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
@ -39,4 +55,12 @@ class ProjectLog extends AbstractModel
|
||||
return $this->hasOne(User::class, 'userid', 'userid');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function projectTask(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(ProjectTask::class, 'id', 'task_id');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,19 +7,22 @@ use App\Module\Base;
|
||||
use App\Tasks\PushTask;
|
||||
use Arr;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Exception;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Request;
|
||||
|
||||
/**
|
||||
* Class ProjectTask
|
||||
* App\Models\ProjectTask
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $parent_id 父级任务ID
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $column_id 列表ID
|
||||
* @property int|null $dialog_id 聊天会话ID
|
||||
* @property int|null $flow_item_id 工作流状态ID
|
||||
* @property string|null $flow_item_name 工作流状态名称
|
||||
* @property string|null $name 标题
|
||||
* @property string|null $color 颜色
|
||||
* @property string|null $desc 描述
|
||||
@ -27,6 +30,7 @@ use Request;
|
||||
* @property string|null $end_at 计划结束时间
|
||||
* @property string|null $archived_at 归档时间
|
||||
* @property int|null $archived_userid 归档会员
|
||||
* @property int|null $archived_follow 跟随项目归档(项目取消归档时任务也取消归档)
|
||||
* @property string|null $complete_at 完成时间
|
||||
* @property int|null $userid 创建人
|
||||
* @property int|null $p_level 优先级
|
||||
@ -40,24 +44,27 @@ use Request;
|
||||
* @property-read int $file_num
|
||||
* @property-read int $msg_num
|
||||
* @property-read bool $overdue
|
||||
* @property-read bool $owner
|
||||
* @property-read int $percent
|
||||
* @property-read int $sub_complete
|
||||
* @property-read int $sub_num
|
||||
* @property-read bool $today
|
||||
* @property-read \App\Models\Project|null $project
|
||||
* @property-read \App\Models\ProjectColumn|null $projectColumn
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskFile[] $taskFile
|
||||
* @property-read int|null $task_file_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskTag[] $taskTag
|
||||
* @property-read int|null $task_tag_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskUser[] $taskUser
|
||||
* @property-read int|null $task_user_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask authData($userid = null)
|
||||
* @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)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectTask onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask query()
|
||||
* @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)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereColor($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereColumnId($value)
|
||||
@ -67,6 +74,8 @@ use Request;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereDesc($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereDialogId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereEndAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereFlowItemId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereFlowItemName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask wherePColor($value)
|
||||
@ -87,7 +96,6 @@ class ProjectTask extends AbstractModel
|
||||
use SoftDeletes;
|
||||
|
||||
protected $appends = [
|
||||
'owner',
|
||||
'file_num',
|
||||
'msg_num',
|
||||
'sub_num',
|
||||
@ -97,22 +105,6 @@ class ProjectTask extends AbstractModel
|
||||
'overdue',
|
||||
];
|
||||
|
||||
/**
|
||||
* 是否我是负责人
|
||||
* @return bool
|
||||
*/
|
||||
public function getOwnerAttribute()
|
||||
{
|
||||
if (!isset($this->appendattrs['owner'])) {
|
||||
if ($this->parent_id > 0) {
|
||||
$this->appendattrs['owner'] = ProjectTaskUser::whereTaskId($this->id)->whereUserid(User::userid())->whereOwner(1)->exists();
|
||||
} else {
|
||||
$this->appendattrs['owner'] = ProjectTaskUser::whereTaskPid($this->id)->whereUserid(User::userid())->whereOwner(1)->exists();
|
||||
}
|
||||
}
|
||||
return $this->appendattrs['owner'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 附件数量
|
||||
* @return int
|
||||
@ -120,7 +112,7 @@ class ProjectTask extends AbstractModel
|
||||
public function getFileNumAttribute()
|
||||
{
|
||||
if (!isset($this->appendattrs['file_num'])) {
|
||||
$this->appendattrs['file_num'] = ProjectTaskFile::whereTaskId($this->id)->count();
|
||||
$this->appendattrs['file_num'] = $this->parent_id > 0 ? 0 : ProjectTaskFile::whereTaskId($this->id)->count();
|
||||
}
|
||||
return $this->appendattrs['file_num'];
|
||||
}
|
||||
@ -230,6 +222,14 @@ class ProjectTask extends AbstractModel
|
||||
return $this->hasOne(Project::class, 'id', 'project_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function projectColumn(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(ProjectColumn::class, 'id', 'column_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
@ -263,16 +263,67 @@ class ProjectTask extends AbstractModel
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询自己的任务
|
||||
* 查询所有任务(与正常查询多返回owner字段)
|
||||
* @param self $query
|
||||
* @param null $userid
|
||||
* @return self
|
||||
*/
|
||||
public function scopeAuthData($query, $userid = null)
|
||||
public function scopeAllData($query, $userid = null)
|
||||
{
|
||||
$userid = $userid ?: User::userid();
|
||||
$query->whereIn('id', function ($qy) use ($userid) {
|
||||
$qy->select('task_pid')->from('project_task_users')->where('userid', $userid);
|
||||
$query
|
||||
->select([
|
||||
'project_tasks.*',
|
||||
'project_task_users.owner'
|
||||
])
|
||||
->leftJoin('project_task_users', function ($leftJoin) use ($userid) {
|
||||
$leftJoin
|
||||
->on('project_task_users.userid', '=', DB::raw($userid))
|
||||
->on('project_tasks.id', '=', 'project_task_users.task_id');
|
||||
});
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询自己负责或参与的任务
|
||||
* @param self $query
|
||||
* @param null $userid
|
||||
* @param null $owner
|
||||
* @return self
|
||||
*/
|
||||
public function scopeAuthData($query, $userid = null, $owner = null)
|
||||
{
|
||||
$userid = $userid ?: User::userid();
|
||||
$query
|
||||
->select([
|
||||
'project_tasks.*',
|
||||
'project_task_users.owner'
|
||||
])
|
||||
->join('project_task_users', 'project_tasks.id', '=', 'project_task_users.task_id')
|
||||
->where('project_task_users.userid', $userid);
|
||||
if ($owner !== null) {
|
||||
$query->where('project_task_users.owner', $owner);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定范围内的任务
|
||||
* @param $query
|
||||
* @param $start
|
||||
* @param $end
|
||||
* @return mixed
|
||||
*/
|
||||
public function scopeBetweenTime($query, $start, $end)
|
||||
{
|
||||
$query->where(function ($q1) use ($start, $end) {
|
||||
$q1->where(function ($q2) use ($start) {
|
||||
$q2->where('project_tasks.start_at', '<=', $start)->where('project_tasks.end_at', '>=', $start);
|
||||
})->orWhere(function ($q2) use ($end) {
|
||||
$q2->where('project_tasks.start_at', '<=', $end)->where('project_tasks.end_at', '>=', $end);
|
||||
})->orWhere(function ($q2) use ($start, $end) {
|
||||
$q2->where('project_tasks.start_at', '>', $start)->where('project_tasks.end_at', '<', $end);
|
||||
});
|
||||
});
|
||||
return $query;
|
||||
}
|
||||
@ -291,11 +342,32 @@ class ProjectTask extends AbstractModel
|
||||
$content = $data['content'];
|
||||
$times = $data['times'];
|
||||
$owner = $data['owner'];
|
||||
$add_assist = intval($data['add_assist']);
|
||||
$subtasks = $data['subtasks'];
|
||||
$p_level = intval($data['p_level']);
|
||||
$p_name = $data['p_name'];
|
||||
$p_color = $data['p_color'];
|
||||
$top = intval($data['top']);
|
||||
$userid = User::userid();
|
||||
//
|
||||
if (ProjectTask::whereProjectId($project_id)
|
||||
->whereNull('project_tasks.complete_at')
|
||||
->whereNull('project_tasks.archived_at')
|
||||
->count() > 2000) {
|
||||
throw new ApiException('项目内未完成任务最多不能超过2000个');
|
||||
}
|
||||
if (ProjectTask::whereColumnId($column_id)
|
||||
->whereNull('project_tasks.complete_at')
|
||||
->whereNull('project_tasks.archived_at')
|
||||
->count() > 500) {
|
||||
throw new ApiException('单个列表未完成任务最多不能超过500个');
|
||||
}
|
||||
if ($parent_id > 0 && ProjectTask::whereParentId($parent_id)
|
||||
->whereNull('project_tasks.complete_at')
|
||||
->whereNull('project_tasks.archived_at')
|
||||
->count() > 50) {
|
||||
throw new ApiException('每个任务的子任务最多不能超过50个');
|
||||
}
|
||||
//
|
||||
$retPre = $parent_id ? '子任务' : '任务';
|
||||
$task = self::createInstance([
|
||||
@ -307,7 +379,7 @@ class ProjectTask extends AbstractModel
|
||||
'p_color' => $p_color,
|
||||
]);
|
||||
if ($content) {
|
||||
$task->desc = Base::getHtml($content);
|
||||
$task->desc = Base::getHtml($content, 100);
|
||||
}
|
||||
// 标题
|
||||
if (empty($name)) {
|
||||
@ -319,11 +391,9 @@ class ProjectTask extends AbstractModel
|
||||
// 时间
|
||||
if ($times) {
|
||||
list($start, $end) = is_string($times) ? explode(",", $times) : (is_array($times) ? $times : []);
|
||||
if (Base::isDate($start) && Base::isDate($end)) {
|
||||
if ($start != $end) {
|
||||
$task->start_at = Carbon::parse($start);
|
||||
$task->end_at = Carbon::parse($end);
|
||||
}
|
||||
if (Base::isDate($start) && Base::isDate($end) && $start != $end) {
|
||||
$task->start_at = Carbon::parse($start);
|
||||
$task->end_at = Carbon::parse($end);
|
||||
}
|
||||
}
|
||||
// 负责人
|
||||
@ -334,20 +404,46 @@ class ProjectTask extends AbstractModel
|
||||
if (!ProjectUser::whereProjectId($project_id)->whereUserid($uid)->exists()) {
|
||||
throw new ApiException($retPre . '负责人填写错误');
|
||||
}
|
||||
if (ProjectTask::authData($uid)
|
||||
->whereNull('project_tasks.complete_at')
|
||||
->whereNull('project_tasks.archived_at')
|
||||
->count() > 500) {
|
||||
throw new ApiException(User::userid2nickname($uid) . '负责或参与的未完成任务最多不能超过500个');
|
||||
}
|
||||
$tmpArray[] = $uid;
|
||||
}
|
||||
$owner = $tmpArray;
|
||||
// 协助人员
|
||||
$assist = [];
|
||||
if (!in_array($userid, $owner) && $add_assist) {
|
||||
$assist = [$userid];
|
||||
}
|
||||
// 创建人
|
||||
$task->userid = User::userid();
|
||||
$task->userid = $userid;
|
||||
// 排序位置
|
||||
if ($top) {
|
||||
$task->sort = intval(self::whereColumnId($task->column_id)->orderBy('sort')->value('sort')) - 1;
|
||||
} else {
|
||||
$task->sort = intval(self::whereColumnId($task->column_id)->orderByDesc('sort')->value('sort')) + 1;
|
||||
}
|
||||
// 工作流
|
||||
$projectFlow = ProjectFlow::whereProjectId($project_id)->orderByDesc('id')->first();
|
||||
if ($projectFlow) {
|
||||
$projectFlowItem = ProjectFlowItem::whereFlowId($projectFlow->id)->orderBy('sort')->get();
|
||||
// 赋一个开始状态
|
||||
foreach ($projectFlowItem as $item) {
|
||||
if ($item->status == 'start') {
|
||||
$task->flow_item_id = $item->id;
|
||||
$task->flow_item_name = $item->status . "|" . $item->name;
|
||||
$owner = array_merge($owner, $item->userids);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
return AbstractModel::transaction(function() use ($subtasks, $content, $owner, $task) {
|
||||
return AbstractModel::transaction(function() use ($assist, $times, $subtasks, $content, $owner, $task) {
|
||||
$task->save();
|
||||
$owner = array_values(array_unique($owner));
|
||||
foreach ($owner as $uid) {
|
||||
ProjectTaskUser::createInstance([
|
||||
'project_id' => $task->project_id,
|
||||
@ -357,6 +453,16 @@ class ProjectTask extends AbstractModel
|
||||
'owner' => 1,
|
||||
])->save();
|
||||
}
|
||||
$assist = array_values(array_unique(array_diff($assist, $owner)));
|
||||
foreach ($assist as $uid) {
|
||||
ProjectTaskUser::createInstance([
|
||||
'project_id' => $task->project_id,
|
||||
'task_id' => $task->id,
|
||||
'task_pid' => $task->parent_id ?: $task->id,
|
||||
'userid' => $uid,
|
||||
'owner' => 0,
|
||||
])->save();
|
||||
}
|
||||
if ($content) {
|
||||
ProjectTaskContent::createInstance([
|
||||
'project_id' => $task->project_id,
|
||||
@ -366,13 +472,24 @@ class ProjectTask extends AbstractModel
|
||||
}
|
||||
if ($task->parent_id == 0 && $subtasks && is_array($subtasks)) {
|
||||
foreach ($subtasks as $subtask) {
|
||||
list($start, $end) = is_string($subtask['times']) ? explode(",", $subtask['times']) : (is_array($subtask['times']) ? $subtask['times'] : []);
|
||||
if (Base::isDate($start) && Base::isDate($end) && $start != $end) {
|
||||
if (Carbon::parse($start)->lt($task->start_at)) {
|
||||
throw new ApiException('子任务开始时间不能小于主任务开始时间');
|
||||
}
|
||||
if (Carbon::parse($end)->gt($task->end_at)) {
|
||||
throw new ApiException('子任务结束时间不能大于主任务结束时间');
|
||||
}
|
||||
} else {
|
||||
$subtask['times'] = $times;
|
||||
}
|
||||
$subtask['parent_id'] = $task->id;
|
||||
$subtask['project_id'] = $task->project_id;
|
||||
$subtask['column_id'] = $task->column_id;
|
||||
self::addTask($subtask);
|
||||
}
|
||||
}
|
||||
$task->addLog("创建{任务}:" . $task->name);
|
||||
$task->addLog("创建{任务}");
|
||||
return $task;
|
||||
});
|
||||
}
|
||||
@ -380,12 +497,124 @@ class ProjectTask extends AbstractModel
|
||||
/**
|
||||
* 修改任务
|
||||
* @param $data
|
||||
* @param $updateContent
|
||||
* @param array $updateMarking 更新的标记
|
||||
* - is_update_project 是否更新项目数据(项目统计)
|
||||
* - is_update_content 是否更新任务详情
|
||||
* - is_update_maintask 是否更新主任务
|
||||
* - is_update_subtask 是否更新子任务
|
||||
* @return bool
|
||||
*/
|
||||
public function updateTask($data, &$updateContent)
|
||||
public function updateTask($data, &$updateMarking = [])
|
||||
{
|
||||
AbstractModel::transaction(function () use ($data, &$updateContent) {
|
||||
AbstractModel::transaction(function () use ($data, &$updateMarking) {
|
||||
// 判断版本
|
||||
Base::checkClientVersion('0.6.0');
|
||||
// 主任务
|
||||
$mainTask = $this->parent_id > 0 ? self::find($this->parent_id) : null;
|
||||
// 工作流
|
||||
if (Arr::exists($data, 'flow_item_id')) {
|
||||
$isProjectOwner = $this->useridInTheProject(User::userid()) === 2;
|
||||
if (!$isProjectOwner && !$this->isOwner()) {
|
||||
throw new ApiException('仅限项目或任务负责人修改任务状态');
|
||||
}
|
||||
if ($this->flow_item_id == $data['flow_item_id']) {
|
||||
throw new ApiException('任务状态未发生改变');
|
||||
}
|
||||
$flowData = [
|
||||
'flow_item_id' => $this->flow_item_id,
|
||||
'flow_item_name' => $this->flow_item_name,
|
||||
];
|
||||
$currentFlowItem = null;
|
||||
$newFlowItem = ProjectFlowItem::whereProjectId($this->project_id)->find(intval($data['flow_item_id']));
|
||||
if (empty($newFlowItem) || empty($newFlowItem->projectFlow)) {
|
||||
throw new ApiException('任务状态不存在');
|
||||
}
|
||||
if ($this->flow_item_id) {
|
||||
// 判断符合流转
|
||||
$currentFlowItem = ProjectFlowItem::find($this->flow_item_id);
|
||||
if ($currentFlowItem) {
|
||||
if (!in_array($newFlowItem->id, $currentFlowItem->turns)) {
|
||||
throw new ApiException("当前状态[{$currentFlowItem->name}]不可流转到[{$newFlowItem->name}]");
|
||||
}
|
||||
if ($currentFlowItem->userlimit) {
|
||||
if (!$isProjectOwner && !in_array(User::userid(), $currentFlowItem->userids)) {
|
||||
throw new ApiException("当前状态[{$currentFlowItem->name}]仅限状态负责人或项目负责人修改");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($newFlowItem->status == 'end') {
|
||||
// 判断自动完成
|
||||
if (!$this->complete_at) {
|
||||
$flowData['complete_at'] = $this->complete_at;
|
||||
$data['complete_at'] = date("Y-m-d H:i");
|
||||
}
|
||||
} else {
|
||||
// 判断自动打开
|
||||
if ($this->complete_at) {
|
||||
$flowData['complete_at'] = $this->complete_at;
|
||||
$data['complete_at'] = false;
|
||||
}
|
||||
}
|
||||
if ($newFlowItem->userids) {
|
||||
// 判断自动添加负责人
|
||||
$flowData['owner'] = $data['owner'] = $this->taskUser->where('owner', 1)->pluck('userid')->toArray();
|
||||
if (in_array($newFlowItem->usertype, ["replace", "merge"])) {
|
||||
// 流转模式、剔除模式
|
||||
if ($this->parent_id === 0) {
|
||||
$flowData['assist'] = $data['assist'] = $this->taskUser->where('owner', 0)->pluck('userid')->toArray();
|
||||
$data['assist'] = array_merge($data['assist'], $data['owner']);
|
||||
}
|
||||
$data['owner'] = $newFlowItem->userids;
|
||||
// 判断剔除模式:保留操作状态的人员
|
||||
if ($newFlowItem->usertype == "merge") {
|
||||
$data['owner'][] = User::userid();
|
||||
}
|
||||
} else {
|
||||
// 添加模式
|
||||
$data['owner'] = array_merge($data['owner'], $newFlowItem->userids);
|
||||
}
|
||||
$data['owner'] = array_values(array_unique($data['owner']));
|
||||
if (isset($data['assist'])) {
|
||||
$data['assist'] = array_values(array_unique(array_diff($data['assist'], $data['owner'])));
|
||||
}
|
||||
}
|
||||
$this->flow_item_id = $newFlowItem->id;
|
||||
$this->flow_item_name = $newFlowItem->status . "|" . $newFlowItem->name;
|
||||
$this->addLog("修改{任务}状态", [
|
||||
'flow' => $flowData,
|
||||
'change' => [$currentFlowItem?->name, $newFlowItem->name]
|
||||
]);
|
||||
ProjectTaskFlowChange::createInstance([
|
||||
'task_id' => $this->id,
|
||||
'userid' => User::userid(),
|
||||
'before_flow_item_id' => $flowData['flow_item_id'],
|
||||
'before_flow_item_name' => $flowData['flow_item_name'],
|
||||
'after_flow_item_id' => $this->flow_item_id,
|
||||
'after_flow_item_name' => $this->flow_item_name,
|
||||
])->save();
|
||||
}
|
||||
// 状态
|
||||
if (Arr::exists($data, 'complete_at')) {
|
||||
// 子任务:主任务已完成时无法修改
|
||||
if ($mainTask?->complete_at) {
|
||||
throw new ApiException('主任务已完成,无法修改子任务状态');
|
||||
}
|
||||
if (Base::isDate($data['complete_at'])) {
|
||||
// 标记已完成
|
||||
if ($this->complete_at) {
|
||||
throw new ApiException('任务已完成');
|
||||
}
|
||||
$this->completeTask(Carbon::now());
|
||||
} else {
|
||||
// 标记未完成
|
||||
if (!$this->complete_at) {
|
||||
throw new ApiException('未完成任务');
|
||||
}
|
||||
$this->completeTask(null);
|
||||
}
|
||||
$updateMarking['is_update_project'] = true;
|
||||
}
|
||||
// 标题
|
||||
if (Arr::exists($data, 'name') && $this->name != $data['name']) {
|
||||
if (empty($data['name'])) {
|
||||
@ -393,14 +622,19 @@ class ProjectTask extends AbstractModel
|
||||
} elseif (mb_strlen($data['name']) > 255) {
|
||||
throw new ApiException('任务描述最多只能设置255个字');
|
||||
}
|
||||
$this->addLog("修改{任务}标题:{$this->name} => {$data['name']}");
|
||||
$this->addLog("修改{任务}标题", [
|
||||
'change' => [$this->name, $data['name']]
|
||||
]);
|
||||
$this->name = $data['name'];
|
||||
}
|
||||
// 负责人
|
||||
if (Arr::exists($data, 'owner')) {
|
||||
$count = $this->taskUser->count();
|
||||
$count = $this->taskUser->where('owner', 1)->count();
|
||||
$array = [];
|
||||
$owner = is_array($data['owner']) ? $data['owner'] : [$data['owner']];
|
||||
if (count($owner) > 10) {
|
||||
throw new ApiException('任务负责人最多不能超过10个');
|
||||
}
|
||||
foreach ($owner as $uid) {
|
||||
if (intval($uid) == 0) continue;
|
||||
if (!$this->project->useridInTheProject($uid)) continue;
|
||||
@ -422,31 +656,90 @@ class ProjectTask extends AbstractModel
|
||||
if ($count == 0 && count($array) == 1 && $array[0] == User::userid()) {
|
||||
$this->addLog("认领{任务}");
|
||||
} else {
|
||||
$this->addLog("修改{任务}负责人:" . implode(",", $array));
|
||||
$this->addLog("修改{任务}负责人", ['userid' => $array]);
|
||||
}
|
||||
}
|
||||
$rows = ProjectTaskUser::whereTaskId($this->id)->whereOwner(1)->whereNotIn('userid', $array)->get();
|
||||
if ($rows->isNotEmpty()) {
|
||||
$this->addLog("删除{任务}负责人:" . $rows->implode('userid', ','));
|
||||
$this->addLog("删除{任务}负责人", ['userid' => $rows->implode('userid', ',')]);
|
||||
foreach ($rows as $row) {
|
||||
$row->delete();
|
||||
}
|
||||
}
|
||||
$updateMarking['is_update_project'] = true;
|
||||
$this->syncDialogUser();
|
||||
}
|
||||
// 计划时间
|
||||
// 计划时间(原则:子任务时间在主任务时间内)
|
||||
if (Arr::exists($data, 'times')) {
|
||||
$oldAt = [Carbon::parse($this->start_at), Carbon::parse($this->end_at)];
|
||||
$oldStringAt = $this->start_at ? ($oldAt[0]->toDateTimeString() . '~' . $oldAt[1]->toDateTimeString()) : '';
|
||||
$this->start_at = null;
|
||||
$this->end_at = null;
|
||||
$times = $data['times'];
|
||||
list($start, $end) = is_string($times) ? explode(",", $times) : (is_array($times) ? $times : []);
|
||||
if (Base::isDate($start) && Base::isDate($end)) {
|
||||
if ($start != $end) {
|
||||
$this->start_at = Carbon::parse($start);
|
||||
$this->end_at = Carbon::parse($end);
|
||||
if (Base::isDate($start) && Base::isDate($end) && $start != $end) {
|
||||
$start_at = Carbon::parse($start);
|
||||
$end_at = Carbon::parse($end);
|
||||
if ($this->parent_id > 0) {
|
||||
// 判断同步主任务时间(子任务时间 超出 主任务)
|
||||
if ($mainTask) {
|
||||
$isUp = false;
|
||||
if ($start_at->lt(Carbon::parse($mainTask->start_at))) {
|
||||
$mainTask->start_at = $start_at;
|
||||
$isUp = true;
|
||||
}
|
||||
if ($end_at->gt(Carbon::parse($mainTask->end_at))) {
|
||||
$mainTask->end_at = $end_at;
|
||||
$isUp = true;
|
||||
}
|
||||
if ($isUp) {
|
||||
$updateMarking['is_update_maintask'] = true;
|
||||
$mainTask->addLog("同步修改{任务}时间");
|
||||
$mainTask->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->start_at = $start_at;
|
||||
$this->end_at = $end_at;
|
||||
} else {
|
||||
if ($this->parent_id > 0) {
|
||||
// 清空子任务时间(子任务时间等于主任务时间)
|
||||
$this->start_at = $mainTask->start_at;
|
||||
$this->end_at = $mainTask->end_at;
|
||||
}
|
||||
}
|
||||
$this->addLog("修改{任务}时间");
|
||||
if ($this->parent_id == 0) {
|
||||
// 判断同步子任务时间(主任务时间 不在 子任务时间 之外)
|
||||
self::whereParentId($this->id)->chunk(100, function($list) use ($oldAt, &$updateMarking) {
|
||||
/** @var self $subTask */
|
||||
foreach ($list as $subTask) {
|
||||
$start_at = Carbon::parse($subTask->start_at);
|
||||
$end_at = Carbon::parse($subTask->end_at);
|
||||
$isUp = false;
|
||||
if (empty($subTask->start_at) || $start_at->eq($oldAt[0]) || $start_at->lt(Carbon::parse($this->start_at))) {
|
||||
$subTask->start_at = $this->start_at;
|
||||
$isUp = true;
|
||||
}
|
||||
if (empty($subTask->end_at) || $end_at->eq($oldAt[1]) || $end_at->gt(Carbon::parse($this->end_at))) {
|
||||
$subTask->end_at = $this->end_at;
|
||||
$isUp = true;
|
||||
}
|
||||
if ($subTask->start_at && Carbon::parse($subTask->start_at)->gt($subTask->end_at)) {
|
||||
$subTask->start_at = $this->start_at;
|
||||
$isUp = true;
|
||||
}
|
||||
if ($isUp) {
|
||||
$updateMarking['is_update_subtask'] = true;
|
||||
$subTask->addLog("同步修改{任务}时间");
|
||||
$subTask->save();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
$newStringAt = $this->start_at ? ($this->start_at->toDateTimeString() . '~' . $this->end_at->toDateTimeString()) : '';
|
||||
$this->addLog("修改{任务}时间", [
|
||||
'change' => [$oldStringAt, $newStringAt]
|
||||
]);
|
||||
}
|
||||
// 以下紧顶级任务可修改
|
||||
if ($this->parent_id === 0) {
|
||||
@ -454,6 +747,9 @@ class ProjectTask extends AbstractModel
|
||||
if (Arr::exists($data, 'assist')) {
|
||||
$array = [];
|
||||
$assist = is_array($data['assist']) ? $data['assist'] : [$data['assist']];
|
||||
if (count($assist) > 10) {
|
||||
throw new ApiException('任务协助人员最多不能超过10个');
|
||||
}
|
||||
foreach ($assist as $uid) {
|
||||
if (intval($uid) == 0) continue;
|
||||
if (!$this->project->useridInTheProject($uid)) continue;
|
||||
@ -463,17 +759,17 @@ class ProjectTask extends AbstractModel
|
||||
'userid' => $uid,
|
||||
], [
|
||||
'project_id' => $this->project_id,
|
||||
'task_pid' => $this->parent_id ?: $this->id,
|
||||
'task_pid' => $this->id,
|
||||
'owner' => 0,
|
||||
]);
|
||||
$array[] = $uid;
|
||||
}
|
||||
if ($array) {
|
||||
$this->addLog("修改{任务}协助人员:" . implode(",", $array));
|
||||
$this->addLog("修改{任务}协助人员", ['userid' => $array]);
|
||||
}
|
||||
$rows = ProjectTaskUser::whereTaskId($this->id)->whereOwner(0)->whereNotIn('userid', $array)->get();
|
||||
if ($rows->isNotEmpty()) {
|
||||
$this->addLog("删除{任务}协助人员:" . $rows->implode('userid', ','));
|
||||
$this->addLog("删除{任务}协助人员", ['userid' => $rows->implode('userid', ',')]);
|
||||
foreach ($rows as $row) {
|
||||
$row->delete();
|
||||
}
|
||||
@ -482,7 +778,9 @@ class ProjectTask extends AbstractModel
|
||||
}
|
||||
// 背景色
|
||||
if (Arr::exists($data, 'color') && $this->color != $data['color']) {
|
||||
$this->addLog("修改{任务}背景色:{$this->color} => {$data['color']}");
|
||||
$this->addLog("修改{任务}背景色", [
|
||||
'change' => [$this->color, $data['color']]
|
||||
]);
|
||||
$this->color = $data['color'];
|
||||
}
|
||||
// 列表
|
||||
@ -492,7 +790,9 @@ class ProjectTask extends AbstractModel
|
||||
if (empty($column)) {
|
||||
throw new ApiException('请选择正确的列表');
|
||||
}
|
||||
$this->addLog("修改{任务}列表:{$oldName} => {$column->name}");
|
||||
$this->addLog("修改{任务}列表", [
|
||||
'change' => [$oldName, $column->name]
|
||||
]);
|
||||
$this->column_id = $column->id;
|
||||
}
|
||||
// 内容
|
||||
@ -503,12 +803,13 @@ class ProjectTask extends AbstractModel
|
||||
], [
|
||||
'content' => $data['content'],
|
||||
]);
|
||||
$this->desc = Base::getHtml($data['content']);
|
||||
$this->desc = Base::getHtml($data['content'], 100);
|
||||
$this->addLog("修改{任务}详细描述");
|
||||
$updateContent = true;
|
||||
$updateMarking['is_update_content'] = true;
|
||||
}
|
||||
// 优先级
|
||||
$p = false;
|
||||
$oldPName = $this->p_name;
|
||||
if (Arr::exists($data, 'p_level') && $this->p_level != $data['p_level']) {
|
||||
$this->p_level = intval($data['p_level']);
|
||||
$p = true;
|
||||
@ -522,7 +823,9 @@ class ProjectTask extends AbstractModel
|
||||
$p = true;
|
||||
}
|
||||
if ($p) {
|
||||
$this->addLog("修改{任务}优先级");
|
||||
$this->addLog("修改{任务}优先级", [
|
||||
'change' => [$oldPName, $this->p_name]
|
||||
]);
|
||||
}
|
||||
}
|
||||
$this->save();
|
||||
@ -539,9 +842,7 @@ class ProjectTask extends AbstractModel
|
||||
{
|
||||
if ($this->parent_id > 0) {
|
||||
$task = self::find($this->parent_id);
|
||||
if ($task) {
|
||||
$task->syncDialogUser();
|
||||
}
|
||||
$task?->syncDialogUser();
|
||||
return;
|
||||
}
|
||||
if (empty($this->dialog_id)) {
|
||||
@ -565,7 +866,7 @@ class ProjectTask extends AbstractModel
|
||||
*/
|
||||
public function relationUserids()
|
||||
{
|
||||
$userids = $this->taskUser->pluck('userid')->toArray();
|
||||
$userids = ProjectTaskUser::whereTaskId($this->id)->orderByDesc('owner')->orderByDesc('id')->pluck('userid')->toArray();
|
||||
$items = ProjectTask::with(['taskUser'])->where('parent_id', $this->id)->whereNull('archived_at')->get();
|
||||
foreach ($items as $item) {
|
||||
$userids = array_merge($userids, $item->taskUser->pluck('userid')->toArray());
|
||||
@ -601,6 +902,74 @@ class ProjectTask extends AbstractModel
|
||||
return $user->owner ? 2 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限版本
|
||||
* @param int $level 1-负责人,2-协助人/负责人,3-创建人/协助人/负责人
|
||||
* @return bool
|
||||
*/
|
||||
public function permission($level = 1)
|
||||
{
|
||||
if ($level >= 3 && $this->isCreater()) {
|
||||
return true;
|
||||
}
|
||||
if ($level >= 2 && $this->isAssister()) {
|
||||
return true;
|
||||
}
|
||||
return $this->isOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否创建者
|
||||
* @return bool
|
||||
*/
|
||||
public function isCreater()
|
||||
{
|
||||
return $this->userid == User::userid();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否协助人员
|
||||
* @return bool
|
||||
*/
|
||||
public function isAssister()
|
||||
{
|
||||
$row = $this;
|
||||
while ($row->parent_id > 0) {
|
||||
$row = self::find($row->parent_id);
|
||||
}
|
||||
return ProjectTaskUser::whereTaskId($row->id)->whereUserid(User::userid())->whereOwner(0)->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否负责人(或者是主任务的负责人)
|
||||
* @return bool
|
||||
*/
|
||||
public function isOwner()
|
||||
{
|
||||
if ($this->owner) {
|
||||
return true;
|
||||
}
|
||||
if ($this->parent_id > 0) {
|
||||
$mainTask = self::allData()->find($this->parent_id);
|
||||
if ($mainTask->owner) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有负责人
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOwner()
|
||||
{
|
||||
if (!isset($this->appendattrs['has_owner'])) {
|
||||
$this->appendattrs['has_owner'] = ProjectTaskUser::whereTaskId($this->id)->whereOwner(1)->exists();
|
||||
}
|
||||
return $this->appendattrs['has_owner'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记已完成、未完成
|
||||
* @param Carbon|null $complete_at 完成时间
|
||||
@ -612,7 +981,7 @@ class ProjectTask extends AbstractModel
|
||||
if ($complete_at === null) {
|
||||
// 标记未完成
|
||||
$this->complete_at = null;
|
||||
$this->addLog("{任务}标记未完成:" . $this->name);
|
||||
$this->addLog("标记{任务}未完成");
|
||||
} else {
|
||||
// 标记已完成
|
||||
if ($this->parent_id == 0) {
|
||||
@ -620,8 +989,11 @@ class ProjectTask extends AbstractModel
|
||||
throw new ApiException('子任务未完成');
|
||||
}
|
||||
}
|
||||
if (!$this->hasOwner()) {
|
||||
throw new ApiException('请先领取任务');
|
||||
}
|
||||
$this->complete_at = $complete_at;
|
||||
$this->addLog("{任务}标记已完成:" . $this->name);
|
||||
$this->addLog("标记{任务}已完成");
|
||||
}
|
||||
$this->save();
|
||||
});
|
||||
@ -633,24 +1005,49 @@ class ProjectTask extends AbstractModel
|
||||
* @param Carbon|null $archived_at 归档时间
|
||||
* @return bool
|
||||
*/
|
||||
public function archivedTask($archived_at)
|
||||
public function archivedTask($archived_at, $isAuto = false)
|
||||
{
|
||||
AbstractModel::transaction(function () use ($archived_at) {
|
||||
if (!$this->complete_at) {
|
||||
$flowItems = ProjectFlowItem::whereProjectId($this->project_id)->whereStatus('end')->pluck('name');
|
||||
if ($flowItems) {
|
||||
$flowItems = implode(",", array_values(array_unique($flowItems->toArray())));
|
||||
}
|
||||
if (empty($flowItems)) {
|
||||
$flowItems = "已完成";
|
||||
}
|
||||
throw new ApiException('仅限【' . $flowItems . '】状态的任务归档');
|
||||
}
|
||||
AbstractModel::transaction(function () use ($isAuto, $archived_at) {
|
||||
if ($archived_at === null) {
|
||||
// 取消归档
|
||||
$this->archived_at = null;
|
||||
$this->addLog("任务取消归档:" . $this->name);
|
||||
$this->pushMsg('add', [
|
||||
'new_column' => null,
|
||||
'task' => ProjectTask::with(['taskUser', 'taskTag'])->find($this->id),
|
||||
]);
|
||||
$this->archived_userid = User::userid();
|
||||
$this->archived_follow = 0;
|
||||
$this->addLog("任务取消归档");
|
||||
} else {
|
||||
// 归档任务
|
||||
if ($isAuto === true) {
|
||||
$logText = "自动任务归档";
|
||||
$userid = 0;
|
||||
} else {
|
||||
$logText = "任务归档";
|
||||
$userid = User::userid();
|
||||
}
|
||||
$this->archived_at = $archived_at;
|
||||
$this->archived_userid = User::userid();
|
||||
$this->addLog("任务归档:" . $this->name);
|
||||
$this->pushMsg('archived');
|
||||
$this->archived_userid = $userid;
|
||||
$this->archived_follow = 0;
|
||||
$this->addLog($logText, [], $userid);
|
||||
}
|
||||
$this->pushMsg('update', [
|
||||
'id' => $this->id,
|
||||
'archived_at' => $this->archived_at,
|
||||
'archived_userid' => $this->archived_userid,
|
||||
]);
|
||||
self::whereParentId($this->id)->update([
|
||||
'archived_at' => $this->archived_at,
|
||||
'archived_userid' => $this->archived_userid,
|
||||
'archived_follow' => $this->archived_follow,
|
||||
]);
|
||||
$this->save();
|
||||
});
|
||||
return true;
|
||||
@ -666,12 +1063,11 @@ class ProjectTask extends AbstractModel
|
||||
AbstractModel::transaction(function () {
|
||||
if ($this->dialog_id) {
|
||||
$dialog = WebSocketDialog::find($this->dialog_id);
|
||||
if ($dialog) {
|
||||
$dialog->deleteDialog();
|
||||
}
|
||||
$dialog?->deleteDialog();
|
||||
}
|
||||
self::whereParentId($this->id)->delete();
|
||||
$this->addLog("删除{任务}");
|
||||
$this->delete();
|
||||
$this->addLog("删除{任务}:" . $this->name);
|
||||
});
|
||||
if ($pushMsg) {
|
||||
$this->pushMsg('delete');
|
||||
@ -682,19 +1078,27 @@ class ProjectTask extends AbstractModel
|
||||
/**
|
||||
* 添加任务日志
|
||||
* @param string $detail
|
||||
* @param array $record
|
||||
* @param int $userid
|
||||
* @return ProjectLog
|
||||
*/
|
||||
public function addLog($detail, $userid = 0)
|
||||
public function addLog($detail, $record = [], $userid = 0)
|
||||
{
|
||||
$detail = str_replace("{任务}", $this->parent_id > 0 ? "子任务" : "任务", $detail);
|
||||
$log = ProjectLog::createInstance([
|
||||
$detail = str_replace("{任务}", $this->parent_id ? "子任务" : "任务", $detail);
|
||||
$array = [
|
||||
'project_id' => $this->project_id,
|
||||
'column_id' => $this->column_id,
|
||||
'task_id' => $this->parent_id ?: $this->id,
|
||||
'userid' => $userid ?: User::userid(),
|
||||
'detail' => $detail,
|
||||
]);
|
||||
];
|
||||
if ($this->parent_id) {
|
||||
$record['subtitle'] = $this->name;
|
||||
}
|
||||
if ($record) {
|
||||
$array['record'] = $record;
|
||||
}
|
||||
$log = ProjectLog::createInstance($array);
|
||||
$log->save();
|
||||
return $log;
|
||||
}
|
||||
@ -702,8 +1106,8 @@ class ProjectTask extends AbstractModel
|
||||
/**
|
||||
* 推送消息
|
||||
* @param string $action
|
||||
* @param array $data 发送内容,默认为[id, parent_id, project_id, column_id, dialog_id]
|
||||
* @param array $userid 指定会员,默认为项目所有成员
|
||||
* @param array|self $data 发送内容,默认为[id, parent_id, project_id, column_id, dialog_id]
|
||||
* @param array $userid 指定会员,默认为项目所有成员
|
||||
*/
|
||||
public function pushMsg($action, $data = null, $userid = null)
|
||||
{
|
||||
@ -718,42 +1122,89 @@ class ProjectTask extends AbstractModel
|
||||
'column_id' => $this->column_id,
|
||||
'dialog_id' => $this->dialog_id,
|
||||
];
|
||||
} elseif ($data instanceof self) {
|
||||
$data = $data->toArray();
|
||||
}
|
||||
//
|
||||
$array = [$userid, []];
|
||||
if ($userid === null) {
|
||||
$userid = $this->project->relationUserids();
|
||||
$array[0] = $this->project->relationUserids();
|
||||
} elseif (!is_array($userid)) {
|
||||
$array[0] = [$userid];
|
||||
}
|
||||
//
|
||||
if (isset($data['owner'])) {
|
||||
$owners = ProjectTaskUser::whereTaskId($data['id'])->whereOwner(1)->pluck('userid')->toArray();
|
||||
$array = [array_intersect($array[0], $owners), array_diff($array[0], $owners)];
|
||||
}
|
||||
foreach ($array as $index => $item) {
|
||||
if ($index > 0) {
|
||||
$data['owner'] = 0;
|
||||
}
|
||||
$params = [
|
||||
'ignoreFd' => Request::header('fd'),
|
||||
'userid' => array_values($item),
|
||||
'msg' => [
|
||||
'type' => 'projectTask',
|
||||
'action' => $action,
|
||||
'data' => $data,
|
||||
]
|
||||
];
|
||||
$task = new PushTask($params, false);
|
||||
Task::deliver($task);
|
||||
}
|
||||
$params = [
|
||||
'ignoreFd' => Request::header('fd'),
|
||||
'userid' => $userid,
|
||||
'msg' => [
|
||||
'type' => 'projectTask',
|
||||
'action' => $action,
|
||||
'data' => $data,
|
||||
]
|
||||
];
|
||||
$task = new PushTask($params, false);
|
||||
Task::deliver($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会员ID获取任务、项目信息(用于判断会员是否存在项目内)
|
||||
* 获取任务
|
||||
* @param $task_id
|
||||
* @return ProjectTask|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null
|
||||
*/
|
||||
public static function oneTask($task_id)
|
||||
{
|
||||
return self::with(['taskUser', 'taskTag'])->allData()->where("project_tasks.id", intval($task_id))->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务(会员有任务权限 或 会员存在项目内)
|
||||
* @param int $task_id
|
||||
* @param bool $archived true:仅限未归档, false:仅限已归档, null:不限制
|
||||
* @param int|bool $permission 0|false:不限制, 1|true:限制项目负责人、任务负责人、协助人员及任务创建者, 2:已有负责人才限制true (子任务时如果是主任务负责人也可以)
|
||||
* @param array $with
|
||||
* @param bool $ignoreArchived 排除已归档
|
||||
* @return self
|
||||
*/
|
||||
public static function userTask($task_id, $with = [], $ignoreArchived = true, &$project = null)
|
||||
public static function userTask($task_id, $archived = true, $permission = 0, $with = [])
|
||||
{
|
||||
$builder = self::with($with)->whereId(intval($task_id));
|
||||
if ($ignoreArchived) {
|
||||
$builder->whereNull('archived_at');
|
||||
}
|
||||
$task = $builder->first();
|
||||
$task = self::with($with)->allData()->where("project_tasks.id", intval($task_id))->first();
|
||||
//
|
||||
if (empty($task)) {
|
||||
throw new ApiException('任务不存在');
|
||||
throw new ApiException('任务不存在', [ 'task_id' => $task_id ], -4002);
|
||||
}
|
||||
if ($archived === true && $task->archived_at != null) {
|
||||
throw new ApiException('任务已归档', [ 'task_id' => $task_id ]);
|
||||
}
|
||||
if ($archived === false && $task->archived_at == null) {
|
||||
throw new ApiException('任务未归档', [ 'task_id' => $task_id ]);
|
||||
}
|
||||
//
|
||||
$project = Project::userProject($task->project_id, $ignoreArchived);
|
||||
try {
|
||||
$project = Project::userProject($task->project_id);
|
||||
} catch (Exception $e) {
|
||||
if ($task->owner === null) {
|
||||
throw new ApiException($e->getMessage(), [ 'task_id' => $task_id ], -4002);
|
||||
}
|
||||
$project = Project::find($task->project_id);
|
||||
if (empty($project)) {
|
||||
throw new ApiException('项目不存在或已被删除', [ 'task_id' => $task_id ], -4002);
|
||||
}
|
||||
}
|
||||
//
|
||||
if ($permission === 2) {
|
||||
$permission = $task->hasOwner() ? 1 : 0;
|
||||
}
|
||||
if (($permission === 1 || $permission === true) && !$project->owner && !$task->permission(3)) {
|
||||
throw new ApiException('仅限项目负责人、任务负责人、协助人员或任务创建者操作');
|
||||
}
|
||||
//
|
||||
return $task;
|
||||
}
|
||||
|
@ -3,9 +3,8 @@
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Class ProjectTaskContent
|
||||
* App\Models\ProjectTaskContent
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $task_id 任务ID
|
||||
|
@ -5,17 +5,16 @@ namespace App\Models;
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* Class ProjectTaskFile
|
||||
* App\Models\ProjectTaskFile
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $task_id 任务ID
|
||||
* @property string|null $name 文件名称
|
||||
* @property int|null $size 文件大小(B)
|
||||
* @property string|null $ext 文件格式
|
||||
* @property string|null $path 文件地址
|
||||
* @property string|null $thumb 缩略图
|
||||
* @property string $path 文件地址
|
||||
* @property string $thumb 缩略图
|
||||
* @property int|null $userid 上传用户ID
|
||||
* @property int|null $download 下载次数
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
|
34
app/Models/ProjectTaskFlowChange.php
Normal file
34
app/Models/ProjectTaskFlowChange.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* App\Models\ProjectTaskFlowChange
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $task_id 任务ID
|
||||
* @property int|null $userid 会员ID
|
||||
* @property int|null $before_flow_item_id (变化前)工作流状态ID
|
||||
* @property string|null $before_flow_item_name (变化前)工作流状态名称
|
||||
* @property int|null $after_flow_item_id (变化后)工作流状态ID
|
||||
* @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|ProjectTaskFlowChange newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange query()
|
||||
* @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)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereBeforeFlowItemName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereTaskId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectTaskFlowChange extends AbstractModel
|
||||
{
|
||||
|
||||
}
|
@ -3,9 +3,8 @@
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Class ProjectTaskTag
|
||||
* App\Models\ProjectTaskTag
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $task_id 任务ID
|
||||
|
@ -3,9 +3,8 @@
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Class ProjectTaskUser
|
||||
* App\Models\ProjectTaskUser
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $task_id 任务ID
|
||||
|
@ -5,13 +5,13 @@ namespace App\Models;
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* Class ProjectUser
|
||||
* App\Models\ProjectUser
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @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 $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \App\Models\Project|null $project
|
||||
@ -22,6 +22,7 @@ use App\Module\Base;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereOwner($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereProjectId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereTopAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
@ -42,12 +43,21 @@ class ProjectUser extends AbstractModel
|
||||
*/
|
||||
public function exitProject()
|
||||
{
|
||||
$tasks = ProjectTask::whereProjectId($this->project_id)->authData($this->userid)->get();
|
||||
foreach ($tasks as $task) {
|
||||
if (ProjectTaskUser::whereTaskId($task->id)->whereUserid($this->userid)->delete()) {
|
||||
$task->syncDialogUser();
|
||||
}
|
||||
}
|
||||
ProjectTaskUser::whereProjectId($this->project_id)
|
||||
->whereUserid($this->userid)
|
||||
->chunk(100, function ($list) {
|
||||
$tastIds = [];
|
||||
foreach ($list as $item) {
|
||||
if (!in_array($item->task_pid, $tastIds)) {
|
||||
$tastIds[] = $item->task_pid;
|
||||
}
|
||||
$item->delete();
|
||||
}
|
||||
$tasks = ProjectTask::whereIn('id', $tastIds)->get();
|
||||
foreach ($tasks as $task) {
|
||||
$task->syncDialogUser();
|
||||
}
|
||||
});
|
||||
$this->delete();
|
||||
}
|
||||
}
|
||||
|
158
app/Models/Report.php
Normal file
158
app/Models/Report.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Traits\Creator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
/**
|
||||
* App\Models\Report
|
||||
*
|
||||
* @property int $id
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property string $title 标题
|
||||
* @property string $type 汇报类型
|
||||
* @property int $userid
|
||||
* @property string $content
|
||||
* @property string $sign 汇报唯一标识
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ReportReceive[] $Receives
|
||||
* @property-read int|null $receives_count
|
||||
* @property-read mixed $receives
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\User[] $receivesUser
|
||||
* @property-read int|null $receives_user_count
|
||||
* @property-read \App\Models\User|null $sendUser
|
||||
* @method static Builder|Report newModelQuery()
|
||||
* @method static Builder|Report newQuery()
|
||||
* @method static Builder|Report query()
|
||||
* @method static Builder|Report whereContent($value)
|
||||
* @method static Builder|Report whereCreatedAt($value)
|
||||
* @method static Builder|Report whereId($value)
|
||||
* @method static Builder|Report whereSign($value)
|
||||
* @method static Builder|Report whereTitle($value)
|
||||
* @method static Builder|Report whereType($value)
|
||||
* @method static Builder|Report whereUpdatedAt($value)
|
||||
* @method static Builder|Report whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Report extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
const WEEKLY = "weekly";
|
||||
const DAILY = "daily";
|
||||
|
||||
protected $fillable = [
|
||||
"title",
|
||||
"type",
|
||||
"userid",
|
||||
"content",
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'receives',
|
||||
];
|
||||
|
||||
public function Receives(): HasMany
|
||||
{
|
||||
return $this->hasMany(ReportReceive::class, "rid");
|
||||
}
|
||||
|
||||
public function receivesUser(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(User::class, ReportReceive::class, "rid", "userid")
|
||||
->withPivot("receive_time", "read");
|
||||
}
|
||||
|
||||
public function sendUser()
|
||||
{
|
||||
return $this->hasOne(User::class, "userid", "userid");
|
||||
}
|
||||
|
||||
public function getTypeAttribute($value): string
|
||||
{
|
||||
return match ($value) {
|
||||
Report::WEEKLY => "周报",
|
||||
Report::DAILY => "日报",
|
||||
default => "",
|
||||
};
|
||||
}
|
||||
|
||||
public function getContentAttribute($value): string
|
||||
{
|
||||
return htmlspecialchars_decode($value);
|
||||
}
|
||||
|
||||
public function getReceivesAttribute()
|
||||
{
|
||||
if (!isset($this->appendattrs['receives'])) {
|
||||
$this->appendattrs['receives'] = empty( $this->receivesUser ) ? [] : array_column($this->receivesUser->toArray(), "userid");
|
||||
}
|
||||
return $this->appendattrs['receives'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单条记录
|
||||
* @param $id
|
||||
* @param User|null $user
|
||||
* @return Report|Builder|Model|object|null
|
||||
* @throw ApiException
|
||||
*/
|
||||
public static function getOne($id, User $user = null)
|
||||
{
|
||||
$user === null && $user = User::auth();
|
||||
$one = self::whereUserid($user->userid)->whereId($id)->first();
|
||||
if ( empty($one) )
|
||||
throw new ApiException("记录不存在");
|
||||
return $one;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后一条提交记录
|
||||
* @param User|null $user
|
||||
* @return Builder|Model|\Illuminate\Database\Query\Builder|object
|
||||
*/
|
||||
public static function getLastOne(User $user = null)
|
||||
{
|
||||
$user === null && $user = User::auth();
|
||||
$one = self::whereUserid($user->userid)->orderByDesc("created_at")->first();
|
||||
if ( empty($one) )
|
||||
throw new ApiException("记录不存在");
|
||||
return $one;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一标识
|
||||
* @param $type
|
||||
* @param $offset
|
||||
* @param Carbon|null $time
|
||||
* @return string
|
||||
*/
|
||||
public static function generateSign($type, $offset, Carbon $time = null): string
|
||||
{
|
||||
$user = User::auth();
|
||||
$now_dt = $time === null ? Carbon::now() : $time;
|
||||
$time_s = match ($type) {
|
||||
Report::WEEKLY => function() use ($now_dt, $offset) {
|
||||
// 如果设置了周期偏移量
|
||||
empty( $offset ) || $now_dt->subWeeks( abs( $offset ) );
|
||||
$now_dt->startOfWeek(); // 设置为当周第一天
|
||||
return $now_dt->year . $now_dt->weekOfYear;
|
||||
},
|
||||
Report::DAILY => function() use ($now_dt, $offset) {
|
||||
// 如果设置了周期偏移量
|
||||
empty( $offset ) || $now_dt->subDays( abs( $offset ) );
|
||||
return $now_dt->format("Ymd");
|
||||
},
|
||||
default => "",
|
||||
};
|
||||
return $user->userid . ( is_callable($time_s) ? $time_s() : "" );
|
||||
}
|
||||
}
|
39
app/Models/ReportReceive.php
Normal file
39
app/Models/ReportReceive.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* App\Models\ReportReceive
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $rid
|
||||
* @property string|null $receive_time 接收时间
|
||||
* @property int $userid 接收人
|
||||
* @property int $read 是否已读
|
||||
* @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|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 whereRid($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ReportReceive whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ReportReceive extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
// 关闭时间戳自动写入
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
"rid",
|
||||
"receive_time",
|
||||
"userid",
|
||||
"read",
|
||||
];
|
||||
}
|
@ -3,9 +3,8 @@
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Class Setting
|
||||
* App\Models\Setting
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property string|null $name
|
||||
* @property string|null $desc 参数描述、备注
|
||||
|
@ -3,9 +3,8 @@
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Class Tmp
|
||||
* App\Models\Tmp
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property string|null $name
|
||||
* @property string|null $value
|
||||
|
@ -9,9 +9,8 @@ use Cache;
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Class User
|
||||
* App\Models\User
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $userid
|
||||
* @property array $identity 身份
|
||||
* @property string|null $az A-Z
|
||||
@ -29,8 +28,10 @@ use Carbon\Carbon;
|
||||
* @property string|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 $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Database\Factories\UserFactory factory(...$parameters)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User query()
|
||||
@ -38,6 +39,7 @@ use Carbon\Carbon;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereChangepass($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedIp($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereDisableAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereEncrypt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereIdentity($value)
|
||||
@ -60,6 +62,7 @@ class User extends AbstractModel
|
||||
protected $primaryKey = 'userid';
|
||||
|
||||
protected $hidden = [
|
||||
'disable_at',
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
@ -145,6 +148,24 @@ class User extends AbstractModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查环境是否允许
|
||||
* @param null $onlyUserid 仅指定会员
|
||||
*/
|
||||
public function checkSystem($onlyUserid = null)
|
||||
{
|
||||
if ($onlyUserid && $onlyUserid != $this->userid) {
|
||||
return;
|
||||
}
|
||||
if (env("PASSWORD_ADMIN") == 'disabled') {
|
||||
if ($this->userid == 1) {
|
||||
throw new ApiException('当前环境禁止此操作');
|
||||
}
|
||||
}
|
||||
if (env("PASSWORD_OWNER") == 'disabled') {
|
||||
throw new ApiException('当前环境禁止此操作');
|
||||
}
|
||||
}
|
||||
|
||||
/** ***************************************************************************************** */
|
||||
/** ***************************************************************************************** */
|
||||
@ -160,18 +181,14 @@ class User extends AbstractModel
|
||||
public static function reg($email, $password, $other = [])
|
||||
{
|
||||
//邮箱
|
||||
if (!Base::isMail($email)) {
|
||||
if (!Base::isEmail($email)) {
|
||||
throw new ApiException('请输入正确的邮箱地址');
|
||||
}
|
||||
if (User::email2userid($email) > 0) {
|
||||
throw new ApiException('邮箱地址已存在');
|
||||
}
|
||||
//密码
|
||||
if (strlen($password) < 6) {
|
||||
throw new ApiException('密码设置不能小于6位数');
|
||||
} elseif (strlen($password) > 32) {
|
||||
throw new ApiException('密码最多只能设置32位数');
|
||||
}
|
||||
self::passwordPolicy($password);
|
||||
//开始注册
|
||||
$encrypt = Base::generatePassword(6);
|
||||
$inArray = [
|
||||
@ -441,4 +458,35 @@ class User extends AbstractModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测密码策略是否符合
|
||||
* @param $password
|
||||
* @return void
|
||||
*/
|
||||
public static function passwordPolicy($password)
|
||||
{
|
||||
if (strlen($password) < 6) {
|
||||
throw new ApiException('密码设置不能小于6位数');
|
||||
}
|
||||
if (strlen($password) > 32) {
|
||||
throw new ApiException('密码最多只能设置32位数');
|
||||
}
|
||||
// 复杂密码
|
||||
$password_policy = Base::settingFind('system', 'password_policy');
|
||||
if ($password_policy == 'complex') {
|
||||
if (preg_match("/^[0-9]+$/", $password)) {
|
||||
throw new ApiException('密码不能全是数字,请包含数字,字母大小写或者特殊字符');
|
||||
}
|
||||
if (preg_match("/^[a-zA-Z]+$/", $password)) {
|
||||
throw new ApiException('密码不能全是字母,请包含数字,字母大小写或者特殊字符');
|
||||
}
|
||||
if (preg_match("/^[0-9A-Z]+$/", $password)) {
|
||||
throw new ApiException('密码不能全是数字+大写字母,密码包含数字,字母大小写或者特殊字符');
|
||||
}
|
||||
if (preg_match("/^[0-9a-z]+$/", $password)) {
|
||||
throw new ApiException('密码不能全是数字+小写字母,密码包含数字,字母大小写或者特殊字符');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,13 @@ namespace App\Models;
|
||||
|
||||
|
||||
/**
|
||||
* Class WebSocket
|
||||
* App\Models\WebSocket
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property string $key
|
||||
* @property string|null $fd
|
||||
* @property int|null $userid
|
||||
* @property string|null $path
|
||||
* @property int|null $userid
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket newModelQuery()
|
||||
|
@ -7,9 +7,8 @@ use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* Class WebSocketDialog
|
||||
* App\Models\WebSocketDialog
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property string|null $type 对话类型
|
||||
* @property string|null $group_type 聊天室类型
|
||||
@ -65,14 +64,14 @@ class WebSocketDialog extends AbstractModel
|
||||
|
||||
/**
|
||||
* 获取对话(同时检验对话身份)
|
||||
* @param $id
|
||||
* @param $dialog_id
|
||||
* @return self
|
||||
*/
|
||||
public static function checkDialog($id)
|
||||
public static function checkDialog($dialog_id)
|
||||
{
|
||||
$dialog = WebSocketDialog::whereId($id)->first();
|
||||
$dialog = WebSocketDialog::find($dialog_id);
|
||||
if (empty($dialog)) {
|
||||
throw new ApiException('对话不存在或已被删除');
|
||||
throw new ApiException('对话不存在或已被删除', ['dialog_id' => $dialog_id], -4003);
|
||||
}
|
||||
//
|
||||
$userid = User::userid();
|
||||
@ -121,10 +120,10 @@ class WebSocketDialog extends AbstractModel
|
||||
break;
|
||||
case "group":
|
||||
if ($dialog->group_type === 'project') {
|
||||
$dialog->group_info = Project::withTrashed()->select(['id', 'name'])->whereDialogId($dialog->id)->first();
|
||||
$dialog->group_info = Project::withTrashed()->select(['id', 'name', 'archived_at', 'deleted_at'])->whereDialogId($dialog->id)->first()?->cancelAppend()->cancelHidden();
|
||||
$dialog->name = $dialog->group_info ? $dialog->group_info->name : '';
|
||||
} elseif ($dialog->group_type === 'task') {
|
||||
$dialog->group_info = ProjectTask::withTrashed()->select(['id', 'name'])->whereDialogId($dialog->id)->first();
|
||||
$dialog->group_info = ProjectTask::withTrashed()->select(['id', 'name', 'complete_at', 'archived_at', 'deleted_at'])->whereDialogId($dialog->id)->first()?->cancelAppend()->cancelHidden();
|
||||
$dialog->name = $dialog->group_info ? $dialog->group_info->name : '';
|
||||
}
|
||||
break;
|
||||
|
@ -8,11 +8,11 @@ use App\Tasks\PushTask;
|
||||
use App\Tasks\WebSocketDialogMsgTask;
|
||||
use Carbon\Carbon;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* Class WebSocketDialogMsg
|
||||
* App\Models\WebSocketDialogMsg
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $dialog_id 对话ID
|
||||
* @property int|null $userid 发送会员ID
|
||||
@ -22,11 +22,15 @@ use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
* @property int|null $send 发送数量
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @property-read int|mixed $percentage
|
||||
* @property-read \App\Models\WebSocketDialog|null $webSocketDialog
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|WebSocketDialogMsg onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg query()
|
||||
* @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)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereMsg($value)
|
||||
@ -35,10 +39,14 @@ use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereType($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUserid($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|WebSocketDialogMsg withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|WebSocketDialogMsg withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class WebSocketDialogMsg extends AbstractModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $appends = [
|
||||
'percentage',
|
||||
];
|
||||
@ -47,6 +55,14 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function webSocketDialog(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(WebSocketDialog::class, 'id', 'dialog_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 阅读占比
|
||||
* @return int|mixed
|
||||
@ -54,11 +70,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
public function getPercentageAttribute()
|
||||
{
|
||||
if (!isset($this->appendattrs['percentage'])) {
|
||||
if ($this->read > $this->send || empty($this->send)) {
|
||||
$this->appendattrs['percentage'] = 100;
|
||||
} else {
|
||||
$this->appendattrs['percentage'] = intval($this->read / $this->send * 100);
|
||||
}
|
||||
$this->generatePercentage();
|
||||
}
|
||||
return $this->appendattrs['percentage'];
|
||||
}
|
||||
@ -82,6 +94,22 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取占比
|
||||
* @param bool $increment 是否新增阅读数
|
||||
* @return int
|
||||
*/
|
||||
public function generatePercentage($increment = false) {
|
||||
if ($increment) {
|
||||
$this->increment('read');
|
||||
}
|
||||
if ($this->read > $this->send || empty($this->send)) {
|
||||
return $this->appendattrs['percentage'] = 100;
|
||||
} else {
|
||||
return $this->appendattrs['percentage'] = intval($this->read / $this->send * 100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记已送达 同时 告诉发送人已送达
|
||||
* @param $userid
|
||||
@ -111,13 +139,17 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
if (!$msgRead->read_at) {
|
||||
$msgRead->read_at = Carbon::now();
|
||||
$msgRead->save();
|
||||
$this->increment('read');
|
||||
$this->generatePercentage(true);
|
||||
PushTask::push([
|
||||
'userid' => $this->userid,
|
||||
'msg' => [
|
||||
'type' => 'dialog',
|
||||
'mode' => 'update',
|
||||
'data' => $this->toArray(),
|
||||
'mode' => 'readed',
|
||||
'data' => [
|
||||
'id' => $this->id,
|
||||
'read' => $this->read,
|
||||
'percentage' => $this->percentage,
|
||||
],
|
||||
]
|
||||
]);
|
||||
}
|
||||
@ -125,6 +157,47 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除消息
|
||||
* @return void
|
||||
*/
|
||||
public function deleteMsg()
|
||||
{
|
||||
$send_dt = Carbon::parse($this->created_at)->addDay();
|
||||
if ($send_dt->lt(Carbon::now())) {
|
||||
throw new ApiException('已超过24小时,此消息不能撤回');
|
||||
}
|
||||
AbstractModel::transaction(function() {
|
||||
$deleteRead = WebSocketDialogMsgRead::whereMsgId($this->id)->whereNull('read_at')->delete(); // 未阅读记录不需要软删除,直接删除即可
|
||||
$this->delete();
|
||||
//
|
||||
$last_msg = null;
|
||||
if ($this->webSocketDialog) {
|
||||
$last_msg = WebSocketDialogMsg::whereDialogId($this->dialog_id)->orderByDesc('id')->first();
|
||||
$this->webSocketDialog->last_at = $last_msg->created_at;
|
||||
$this->webSocketDialog->save();
|
||||
}
|
||||
//
|
||||
$dialog = WebSocketDialog::find($this->dialog_id);
|
||||
if ($dialog) {
|
||||
$userids = $dialog->dialogUser->pluck('userid')->toArray();
|
||||
PushTask::push([
|
||||
'userid' => $userids,
|
||||
'msg' => [
|
||||
'type' => 'dialog',
|
||||
'mode' => 'delete',
|
||||
'data' => [
|
||||
'id' => $this->id,
|
||||
'dialog_id' => $this->dialog_id,
|
||||
'last_msg' => $last_msg,
|
||||
'update_read' => $deleteRead ? 1 : 0
|
||||
],
|
||||
]
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @param int $dialog_id 会话ID(即 聊天室ID)
|
||||
|
@ -3,9 +3,8 @@
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Class WebSocketDialogMsgRead
|
||||
* App\Models\WebSocketDialogMsgRead
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $dialog_id 对话ID
|
||||
* @property int|null $msg_id 消息ID
|
||||
|
@ -3,12 +3,12 @@
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Class WebSocketDialogUser
|
||||
* App\Models\WebSocketDialogUser
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $dialog_id 对话ID
|
||||
* @property int|null $userid 会员ID
|
||||
* @property string|null $top_at 置顶时间
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newModelQuery()
|
||||
@ -17,6 +17,7 @@ namespace App\Models;
|
||||
* @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 whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereTopAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
|
@ -3,9 +3,8 @@
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Class WebSocketTmpMsg
|
||||
* App\Models\WebSocketTmpMsg
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property string|null $md5 MD5(会员ID-消息)
|
||||
* @property string|null $msg 详细消息
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Module;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Tmp;
|
||||
use Cache;
|
||||
@ -74,6 +75,31 @@ class Base
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端版本号
|
||||
* @return string
|
||||
*/
|
||||
public static function getClientVersion()
|
||||
{
|
||||
global $_A;
|
||||
if (!isset($_A["__static_client_version"])) {
|
||||
$_A["__static_client_version"] = Request::header('version') ?: '0.0.1';
|
||||
}
|
||||
return $_A["__static_client_version"];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查客户端版本
|
||||
* @param string $min 最小版本
|
||||
* @return void
|
||||
*/
|
||||
public static function checkClientVersion($min)
|
||||
{
|
||||
if (version_compare(Base::getClientVersion(), $min, '<')) {
|
||||
throw new ApiException('当前版本 (v' . Base::getClientVersion() . ') 过低');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否域名格式
|
||||
* @param $domain
|
||||
@ -316,19 +342,15 @@ class Base
|
||||
{
|
||||
if (strtolower($charset) == 'utf-8') {
|
||||
if (Base::getStrlen($string) <= $length) return $string;
|
||||
$strcut = str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $string);
|
||||
$strcut = Base::utf8Substr($strcut, $length, $start);
|
||||
$strcut = str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $strcut);
|
||||
$strcut = Base::utf8Substr($string, $length, $start);
|
||||
return $strcut . $dot;
|
||||
} else {
|
||||
$length = $length * 2;
|
||||
if (strlen($string) <= $length) return $string;
|
||||
$string = str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $string);
|
||||
$strcut = '';
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$strcut .= ord($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i];
|
||||
}
|
||||
$strcut = str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $strcut);
|
||||
}
|
||||
return $strcut . $dot;
|
||||
}
|
||||
@ -725,6 +747,7 @@ class Base
|
||||
*/
|
||||
public static function fillUrl($str = '')
|
||||
{
|
||||
global $_A;
|
||||
if (is_array($str)) {
|
||||
foreach ($str as $key => $item) {
|
||||
$str[$key] = Base::fillUrl($item);
|
||||
@ -743,9 +766,12 @@ class Base
|
||||
) {
|
||||
return $str;
|
||||
} else {
|
||||
if ($_A['__fill_url_remote_url'] === true) {
|
||||
return "{{RemoteURL}}" . $str;
|
||||
}
|
||||
try {
|
||||
return url($str);
|
||||
} catch (\Throwable $e) {
|
||||
} catch (\Throwable) {
|
||||
return self::getSchemeAndHost() . "/" . $str;
|
||||
}
|
||||
}
|
||||
@ -766,7 +792,7 @@ class Base
|
||||
}
|
||||
try {
|
||||
$find = url('');
|
||||
} catch (\Throwable $e) {
|
||||
} catch (\Throwable) {
|
||||
$find = self::getSchemeAndHost();
|
||||
}
|
||||
return Base::leftDelete($str, $find . '/');
|
||||
@ -782,6 +808,31 @@ class Base
|
||||
return $scheme.($_SERVER['HTTP_HOST'] ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 地址后拼接参数
|
||||
* @param $url
|
||||
* @param $parames
|
||||
* @return mixed|string
|
||||
*/
|
||||
public static function urlAddparameter($url, $parames)
|
||||
{
|
||||
if ($parames && is_array($parames)) {
|
||||
$array = [];
|
||||
foreach ($parames as $key => $val) {
|
||||
$array[] = $key . "=" . $val;
|
||||
}
|
||||
if ($array) {
|
||||
$query = implode("&", $array);
|
||||
if (str_contains($url, "?")) {
|
||||
$url .= "&" . $query;
|
||||
} else {
|
||||
$url .= "?" . $query;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化内容图片地址
|
||||
* @param $content
|
||||
@ -830,13 +881,16 @@ class Base
|
||||
/**
|
||||
* 数组只保留数字的
|
||||
* @param $array
|
||||
* @param bool $int 是否格式化值
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayRetainInt($array)
|
||||
public static function arrayRetainInt($array, $int = false)
|
||||
{
|
||||
foreach ($array as $k => $v) {
|
||||
if (!is_numeric($v)) {
|
||||
unset($array[$k]);
|
||||
} elseif ($int === true) {
|
||||
$array[$k] = intval($v);
|
||||
}
|
||||
}
|
||||
return array_values($array);
|
||||
@ -934,7 +988,7 @@ class Base
|
||||
* @param string $str 需要检测的字符串
|
||||
* @return int
|
||||
*/
|
||||
public static function isMail($str)
|
||||
public static function isEmail($str)
|
||||
{
|
||||
$RegExp = '/^[a-z0-9][a-z\.0-9-_]+@[a-z0-9_-]+(?:\.[a-z]{0,3}\.[a-z]{0,2}|\.[a-z]{0,3}|\.[a-z]{0,2})$/i';
|
||||
return preg_match($RegExp, $str);
|
||||
@ -2216,10 +2270,7 @@ class Base
|
||||
$type = ['zip'];
|
||||
break;
|
||||
case 'file':
|
||||
$type = ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz'];
|
||||
break;
|
||||
case 'office':
|
||||
$type = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'];
|
||||
$type = ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz', 'ai', 'avi', 'bmp', 'cdr', 'eps', 'mov', 'mp3', 'mp4', 'pr', 'psd', 'svg', 'tif'];
|
||||
break;
|
||||
case 'firmware':
|
||||
$type = ['img', 'tar', 'bin'];
|
||||
@ -2227,8 +2278,31 @@ class Base
|
||||
case 'md':
|
||||
$type = ['md'];
|
||||
break;
|
||||
case 'node_template':
|
||||
$type = ['csv'];
|
||||
case 'more':
|
||||
$type = [
|
||||
'text', 'md', 'markdown',
|
||||
'drawio',
|
||||
'mind',
|
||||
'docx', 'wps', 'doc', 'xls', 'xlsx', 'ppt', 'pptx',
|
||||
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'ico', 'raw',
|
||||
'rar', 'zip', 'jar', '7-zip', 'tar', 'gzip', '7z',
|
||||
'tif', 'tiff',
|
||||
'dwg', 'dxf',
|
||||
'ofd',
|
||||
'pdf',
|
||||
'txt',
|
||||
'htaccess', 'htgroups', 'htpasswd', 'conf', 'bat', 'cmd', 'cpp', 'c', 'cc', 'cxx', 'h', 'hh', 'hpp', 'ino', 'cs', 'css',
|
||||
'dockerfile', 'go', 'html', 'htm', 'xhtml', 'vue', 'we', 'wpy', 'java', 'js', 'jsm', 'jsx', 'json', 'jsp', 'less', 'lua', 'makefile', 'gnumakefile',
|
||||
'ocamlmakefile', 'make', 'mysql', 'nginx', 'ini', 'cfg', 'prefs', 'm', 'mm', 'pl', 'pm', 'p6', 'pl6', 'pm6', 'pgsql', 'php',
|
||||
'inc', 'phtml', 'shtml', 'php3', 'php4', 'php5', 'phps', 'phpt', 'aw', 'ctp', 'module', 'ps1', 'py', 'r', 'rb', 'ru', 'gemspec', 'rake', 'guardfile', 'rakefile',
|
||||
'gemfile', 'rs', 'sass', 'scss', 'sh', 'bash', 'bashrc', 'sql', 'sqlserver', 'swift', 'ts', 'typescript', 'str', 'vbs', 'vb', 'v', 'vh', 'sv', 'svh', 'xml',
|
||||
'rdf', 'rss', 'wsdl', 'xslt', 'atom', 'mathml', 'mml', 'xul', 'xbl', 'xaml', 'yaml', 'yml',
|
||||
'asp', 'properties', 'gitignore', 'log', 'bas', 'prg', 'python', 'ftl', 'aspx',
|
||||
'mp3', 'wav', 'mp4', 'flv',
|
||||
'avi', 'mov', 'wmv', 'mkv', '3gp', 'rm',
|
||||
'xmind',
|
||||
'rp',
|
||||
];
|
||||
break;
|
||||
default:
|
||||
return Base::retError('错误的类型参数');
|
||||
@ -2349,6 +2423,37 @@ class Base
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件移动
|
||||
* @param array $uploadResult
|
||||
* @param string $newPath "/" 结尾
|
||||
* @return array
|
||||
*/
|
||||
public static function uploadMove($uploadResult, $newPath)
|
||||
{
|
||||
if (str_ends_with($newPath, "/") && file_exists($uploadResult['file'])) {
|
||||
Base::makeDir(public_path($newPath));
|
||||
$oldPath = dirname($uploadResult['path']) . "/";
|
||||
$newFile = str_replace($oldPath, $newPath, $uploadResult['file']);
|
||||
if (rename($uploadResult['file'], $newFile)) {
|
||||
$oldUrl = $uploadResult['url'];
|
||||
$uploadResult['file'] = $newFile;
|
||||
$uploadResult['path'] = str_replace($oldPath, $newPath, $uploadResult['path']);
|
||||
$uploadResult['url'] = str_replace($oldPath, $newPath, $uploadResult['url']);
|
||||
if ($uploadResult['thumb'] == $oldUrl) {
|
||||
$uploadResult['thumb'] = $uploadResult['url'];
|
||||
} elseif ($uploadResult['thumb']) {
|
||||
$oldThumb = substr($uploadResult['thumb'], strpos($uploadResult['thumb'], $newPath));
|
||||
$newThumb = str_replace($oldPath, $newPath, $oldThumb);
|
||||
if (file_exists(public_path($oldThumb)) && rename(public_path($oldThumb), public_path($newThumb))) {
|
||||
$uploadResult['thumb'] = str_replace($oldPath, $newPath, $uploadResult['thumb']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $uploadResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成缩略图
|
||||
* @param string $src_img 源图绝对完整地址{带文件名及后缀名}
|
||||
@ -2858,4 +2963,19 @@ class Base
|
||||
$matrix = array_unique($matrix, SORT_REGULAR);
|
||||
return array_merge($matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除emoji表情
|
||||
* @param $str
|
||||
* @return string|string[]|null
|
||||
*/
|
||||
public static function filterEmoji($str)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/./u',
|
||||
function (array $match) {
|
||||
return strlen($match[0]) >= 4 ? '' : $match[0];
|
||||
},
|
||||
$str);
|
||||
}
|
||||
}
|
||||
|
144
app/Module/BillExport.php
Normal file
144
app/Module/BillExport.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace App\Module;
|
||||
|
||||
use Excel;
|
||||
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithEvents;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;
|
||||
use Maatwebsite\Excel\Concerns\WithTitle;
|
||||
use Maatwebsite\Excel\Events\AfterSheet;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Exception;
|
||||
|
||||
class BillExport implements WithHeadings, WithEvents, FromCollection, WithTitle, WithStrictNullComparison
|
||||
{
|
||||
public $title;
|
||||
public $headings = [];
|
||||
public $data = [];
|
||||
public $typeLists = [];
|
||||
public $typeNumber = 0;
|
||||
|
||||
public function __construct($title, array $data)
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public static function create($data = [], $title = "Sheet1") {
|
||||
if (is_string($data)) {
|
||||
list($title, $data) = [$data, $title];
|
||||
}
|
||||
if (!is_array($data)) {
|
||||
$data = [];
|
||||
}
|
||||
return new BillExport($title, $data);
|
||||
}
|
||||
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setHeadings(array $headings) {
|
||||
$this->headings = $headings;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setData(array $data) {
|
||||
$this->data = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setTypeList(array $typeList, $number = 0) {
|
||||
$this->typeLists = $typeList;
|
||||
$this->typeNumber = $number;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function store($fileName = '') {
|
||||
if (empty($fileName)) {
|
||||
$fileName = date("YmdHis") . '.xls';
|
||||
}
|
||||
try {
|
||||
return Excel::store($this, $fileName);
|
||||
} catch (Exception $e) {
|
||||
return "导出错误:" . $e->getMessage();
|
||||
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
|
||||
return "导出错误:" . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public function download($fileName = '') {
|
||||
if (empty($fileName)) {
|
||||
$fileName = date("YmdHis") . '.xls';
|
||||
}
|
||||
try {
|
||||
return Excel::download($this, $fileName);
|
||||
} catch (Exception $e) {
|
||||
return "导出错误:" . $e->getMessage();
|
||||
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
|
||||
return "导出错误:" . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出的文件标题
|
||||
* @return string
|
||||
*/
|
||||
public function title(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标题行
|
||||
* @return array
|
||||
*/
|
||||
public function headings(): array
|
||||
{
|
||||
return $this->headings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出的内容
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function collection()
|
||||
{
|
||||
return collect($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置单元格事件
|
||||
* @return array
|
||||
*/
|
||||
public function registerEvents(): array
|
||||
{
|
||||
return [
|
||||
AfterSheet::Class => function (AfterSheet $event) {
|
||||
$count = count($this->data);
|
||||
foreach ($this->typeLists AS $cell => $typeList) {
|
||||
if ($cell && $typeList) {
|
||||
$p = $this->headings ? 1 : 0;
|
||||
for ($i = 1 + $p; $i <= max($count, $this->typeNumber) + $p; $i++) {
|
||||
$validation = $event->sheet->getDelegate()->getCell($cell . $i)->getDataValidation();
|
||||
$validation->setType(DataValidation::TYPE_LIST);
|
||||
$validation->setErrorStyle(DataValidation::STYLE_WARNING);
|
||||
$validation->setAllowBlank(false);
|
||||
$validation->setShowDropDown(true);
|
||||
$validation->setShowInputMessage(true);
|
||||
$validation->setShowErrorMessage(true);
|
||||
$validation->setErrorTitle('输入的值不合法');
|
||||
$validation->setError('选择的值不在列表中,请选择列表中的值');
|
||||
$validation->setPromptTitle('从列表中选择');
|
||||
$validation->setPrompt('请选择下拉列表中的值');
|
||||
$validation->setFormula1('"' . implode(',', $typeList) . '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
16
app/Module/BillImport.php
Normal file
16
app/Module/BillImport.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Module;
|
||||
|
||||
|
||||
use Maatwebsite\Excel\Concerns\ToArray;
|
||||
|
||||
class BillImport implements ToArray
|
||||
{
|
||||
public function Array(Array $tables)
|
||||
{
|
||||
return $tables;
|
||||
}
|
||||
|
||||
}
|
@ -23,6 +23,14 @@ class AppServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
\Illuminate\Database\Query\Builder::macro('rawSql', function(){
|
||||
return array_reduce($this->getBindings(), function($sql, $binding){
|
||||
return preg_replace('/\?/', is_numeric($binding) ? $binding : "'".$binding."'" , $sql, 1);
|
||||
}, $this->toSql());
|
||||
});
|
||||
|
||||
\Illuminate\Database\Eloquent\Builder::macro('rawSql', function(){
|
||||
return ($this->getQuery()->rawSql());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -127,9 +127,11 @@ class WebSocketService implements WebSocketHandlerInterface
|
||||
case 'readMsg':
|
||||
$ids = is_array($data['id']) ? $data['id'] : [$data['id']];
|
||||
$userid = $this->getUserid($frame->fd);
|
||||
$list = WebSocketDialogMsg::whereIn('id', $ids)->get();
|
||||
$list->transform(function(WebSocketDialogMsg $item) use ($userid) {
|
||||
$item->readSuccess($userid);
|
||||
WebSocketDialogMsg::whereIn('id', $ids)->chunkById(20, function($list) use ($userid) {
|
||||
/** @var WebSocketDialogMsg $item */
|
||||
foreach ($list as $item) {
|
||||
$item->readSuccess($userid);
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
||||
@ -204,7 +206,19 @@ class WebSocketService implements WebSocketHandlerInterface
|
||||
*/
|
||||
private function deleteUser($fd)
|
||||
{
|
||||
WebSocket::whereFd($fd)->delete();
|
||||
$array = [];
|
||||
WebSocket::whereFd($fd)->chunk(10, function($list) use (&$array) {
|
||||
/** @var WebSocket $item */
|
||||
foreach ($list as $item) {
|
||||
$item->delete();
|
||||
if ($item->path && str_starts_with($item->path, "file/content/")) {
|
||||
$array[$item->path] = $item->path;
|
||||
}
|
||||
}
|
||||
});
|
||||
foreach ($array as $path) {
|
||||
$this->pushPath($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
45
app/Tasks/AutoArchivedTask.php
Normal file
45
app/Tasks/AutoArchivedTask.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace App\Tasks;
|
||||
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
use App\Models\AbstractModel;
|
||||
use App\Models\ProjectTask;
|
||||
use App\Module\Base;
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* 完成的任务自动归档
|
||||
* Class AutoArchivedTask
|
||||
* @package App\Tasks
|
||||
*/
|
||||
class AutoArchivedTask extends AbstractTask
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$setting = Base::setting('system');
|
||||
if ($setting['auto_archived'] === 'open') {
|
||||
$archivedDay = floatval($setting['archived_day']);
|
||||
if ($archivedDay > 0) {
|
||||
$archivedDay = min(100, $archivedDay);
|
||||
$archivedTime = Carbon::now()->subDays($archivedDay);
|
||||
//获取已完成未归档的任务
|
||||
$taskLists = ProjectTask::whereNotNull('complete_at')
|
||||
->where('complete_at', '<=', $archivedTime)
|
||||
->where('archived_userid', 0)
|
||||
->whereNull('archived_at')
|
||||
->take(100)
|
||||
->get();
|
||||
foreach ($taskLists AS $task) {
|
||||
$task->archivedTask(Carbon::now(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,11 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
|
||||
public function start()
|
||||
{
|
||||
global $_A;
|
||||
$_A = [
|
||||
'__fill_url_remote_url' => true,
|
||||
];
|
||||
//
|
||||
$msg = WebSocketDialogMsg::find($this->id);
|
||||
if (empty($msg)) {
|
||||
return;
|
||||
|
323
cmd
323
cmd
@ -12,6 +12,8 @@ OK="${Green}[OK]${Font}"
|
||||
Error="${Red}[错误]${Font}"
|
||||
|
||||
cur_path="$(pwd)"
|
||||
cur_arg=$@
|
||||
COMPOSE="docker-compose"
|
||||
|
||||
judge() {
|
||||
if [[ 0 -eq $? ]]; then
|
||||
@ -23,16 +25,25 @@ judge() {
|
||||
fi
|
||||
}
|
||||
|
||||
rand(){
|
||||
rand() {
|
||||
local min=$1
|
||||
local max=$(($2-$min+1))
|
||||
local num=$(($RANDOM+1000000000))
|
||||
echo $(($num%$max+$min))
|
||||
}
|
||||
|
||||
rand_string() {
|
||||
local lan=$1
|
||||
if [[ `uname` == 'Linux' ]]; then
|
||||
echo "$(date +%s%N | md5sum | cut -c 1-${lan})"
|
||||
else
|
||||
echo "$(docker run -it --rm alpine sh -c "date +%s%N | md5sum | cut -c 1-${lan}")"
|
||||
fi
|
||||
}
|
||||
|
||||
supervisorctl_restart() {
|
||||
local RES=`run_exec php "supervisorctl update $1"`
|
||||
if [ -z "$RES" ];then
|
||||
if [ -z "$RES" ]; then
|
||||
run_exec php "supervisorctl restart $1"
|
||||
else
|
||||
echo -e "$RES"
|
||||
@ -45,24 +56,36 @@ check_docker() {
|
||||
echo -e "${Error} ${RedBG} 未安装 Docker!${Font}"
|
||||
exit 1
|
||||
fi
|
||||
docker-compose --version &> /dev/null
|
||||
docker-compose version &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${Error} ${RedBG} 未安装 Docker-compose!${Font}"
|
||||
docker compose version &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${Error} ${RedBG} 未安装 Docker-compose!${Font}"
|
||||
exit 1
|
||||
fi
|
||||
COMPOSE="docker compose"
|
||||
fi
|
||||
if [[ -n `$COMPOSE version | grep -E "\sv*1"` ]]; then
|
||||
$COMPOSE version
|
||||
echo -e "${Error} ${RedBG} Docker-compose 版本过低,请升级至v2+!${Font}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_node() {
|
||||
npm --version > /dev/null
|
||||
npm --version &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${Error} ${RedBG} 未安装nodejs!${Font}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
docker_name() {
|
||||
echo `$COMPOSE ps | awk '{print $1}' | grep "\-$1\-"`
|
||||
}
|
||||
|
||||
run_compile() {
|
||||
local type=$1
|
||||
local npxcmd=""
|
||||
check_node
|
||||
if [ ! -d "./node_modules" ]; then
|
||||
npm install
|
||||
@ -70,37 +93,91 @@ run_compile() {
|
||||
run_exec php "php bin/run --mode=$type"
|
||||
supervisorctl_restart php
|
||||
#
|
||||
mix -V &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
npxcmd="npx"
|
||||
fi
|
||||
if [ "$type" = "prod" ]; then
|
||||
rm -rf "./public/js/build"
|
||||
$npxcmd mix --production
|
||||
npx mix --production
|
||||
else
|
||||
$npxcmd mix watch --hot
|
||||
npx mix watch --hot
|
||||
fi
|
||||
}
|
||||
|
||||
run_electron() {
|
||||
local argv=$@
|
||||
check_node
|
||||
if [ ! -d "./node_modules" ]; then
|
||||
npm install
|
||||
fi
|
||||
if [ ! -d "./electron/node_modules" ]; then
|
||||
pushd electron
|
||||
npm install
|
||||
popd
|
||||
fi
|
||||
#
|
||||
if [ -d "./electron/dist" ]; then
|
||||
rm -rf "./electron/dist"
|
||||
fi
|
||||
if [ -d "./electron/public" ] && [ "$argv" != "--nobuild" ]; then
|
||||
rm -rf "./electron/public"
|
||||
fi
|
||||
mkdir -p ./electron/public
|
||||
cp ./electron/index.html ./electron/public/index.html
|
||||
#
|
||||
if [ "$argv" != "dev" ] && [ "$argv" != "--nobuild" ]; then
|
||||
npx mix --production -- --env --electron
|
||||
fi
|
||||
node ./electron/build.js $argv
|
||||
}
|
||||
|
||||
run_exec() {
|
||||
local container=$1
|
||||
local cmd=$2
|
||||
local name=`get_docker_name $container`
|
||||
if [ "$container" = "mariadb" ]; then
|
||||
docker exec -it "$name" /bin/sh -c "$cmd"
|
||||
else
|
||||
docker exec -it "$name" /bin/bash -c "$cmd"
|
||||
fi
|
||||
}
|
||||
|
||||
get_docker_name() {
|
||||
local container=$1
|
||||
local name=`docker-compose ps | awk '{print $1}' | grep "\-$container\-"`
|
||||
local name=`docker_name $container`
|
||||
if [ -z "$name" ]; then
|
||||
echo -e "${Error} ${RedBG} 没有找到 $container 容器! ${Font}"
|
||||
exit 1
|
||||
fi
|
||||
echo $name
|
||||
docker exec -it "$name" /bin/sh -c "$cmd"
|
||||
}
|
||||
|
||||
run_mysql() {
|
||||
if [ "$1" = "backup" ]; then
|
||||
# 备份数据库
|
||||
database=$(env_get DB_DATABASE)
|
||||
username=$(env_get DB_USERNAME)
|
||||
password=$(env_get DB_PASSWORD)
|
||||
mkdir -p ${cur_path}/docker/mysql/backup
|
||||
filename="${cur_path}/docker/mysql/backup/${database}_$(date "+%Y%m%d%H%M%S").sql.gz"
|
||||
run_exec mariadb "exec mysqldump --databases $database -u$username -p$password" | gzip > $filename
|
||||
judge "备份数据库"
|
||||
[ -f "$filename" ] && echo -e "备份文件:$filename"
|
||||
elif [ "$1" = "recovery" ]; then
|
||||
# 还原数据库
|
||||
database=$(env_get DB_DATABASE)
|
||||
username=$(env_get DB_USERNAME)
|
||||
password=$(env_get DB_PASSWORD)
|
||||
mkdir -p ${cur_path}/docker/mysql/backup
|
||||
list=`ls -1 "${cur_path}/docker/mysql/backup" | grep ".sql.gz"`
|
||||
if [ -z "$list" ]; then
|
||||
echo -e "${Error} ${RedBG} 没有备份文件!${Font}"
|
||||
exit 1
|
||||
fi
|
||||
echo "$list"
|
||||
read -rp "请输入备份文件名称还原:" inputname
|
||||
filename="${cur_path}/docker/mysql/backup/${inputname}"
|
||||
if [ ! -f "$filename" ]; then
|
||||
echo -e "${Error} ${RedBG} 备份文件:${inputname} 不存在! ${Font}"
|
||||
exit 1
|
||||
fi
|
||||
container_name=`docker_name mariadb`
|
||||
if [ -z "$container_name" ]; then
|
||||
echo -e "${Error} ${RedBG} 没有找到 mariadb 容器! ${Font}"
|
||||
exit 1
|
||||
fi
|
||||
docker cp $filename $container_name:/
|
||||
run_exec mariadb "gunzip < /$inputname | mysql -u$username -p$password $database"
|
||||
run_exec php "php artisan migrate"
|
||||
judge "还原数据库"
|
||||
fi
|
||||
}
|
||||
|
||||
env_get() {
|
||||
@ -113,11 +190,14 @@ env_set() {
|
||||
local key=$1
|
||||
local val=$2
|
||||
local exist=`cat ${cur_path}/.env | grep "^$key="`
|
||||
if [ -z "$exist" ];then
|
||||
if [ -z "$exist" ]; then
|
||||
echo "$key=$val" >> $cur_path/.env
|
||||
else
|
||||
command="sed -i '/^$key=/c\\$key=$val' /www/.env"
|
||||
docker run -it --rm -v ${cur_path}:/www alpine sh -c "$command"
|
||||
if [[ `uname` == 'Linux' ]]; then
|
||||
sed -i "/^${key}=/c\\${key}=${val}" ${cur_path}/.env
|
||||
else
|
||||
docker run -it --rm -v ${cur_path}:/www alpine sh -c "sed -i "/^${key}=/c\\${key}=${val}" /www/.env"
|
||||
fi
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${Error} ${RedBG} 设置env参数失败!${Font}"
|
||||
exit 1
|
||||
@ -126,51 +206,120 @@ env_set() {
|
||||
}
|
||||
|
||||
env_init() {
|
||||
if [ ! -f ".env" ];then
|
||||
if [ ! -f ".env" ]; then
|
||||
cp .env.docker .env
|
||||
fi
|
||||
if [ -z "$(env_get DB_ROOT_PASSWORD)" ];then
|
||||
env_set DB_ROOT_PASSWORD "$(docker run -it --rm alpine sh -c "date +%s%N | md5sum | cut -c 1-16")"
|
||||
if [ -z "$(env_get DB_ROOT_PASSWORD)" ]; then
|
||||
env_set DB_ROOT_PASSWORD "$(rand_string 16)"
|
||||
fi
|
||||
if [ -z "$(env_get APP_ID)" ];then
|
||||
env_set APP_ID "$(docker run -it --rm alpine sh -c "date +%s%N | md5sum | cut -c 1-6")"
|
||||
if [ -z "$(env_get APP_ID)" ]; then
|
||||
env_set APP_ID "$(rand_string 6)"
|
||||
fi
|
||||
if [ -z "$(env_get APP_IPPR)" ];then
|
||||
if [ -z "$(env_get APP_IPPR)" ]; then
|
||||
env_set APP_IPPR "10.$(rand 50 100).$(rand 100 200)"
|
||||
fi
|
||||
}
|
||||
|
||||
arg_get() {
|
||||
local find="n"
|
||||
local value=""
|
||||
for var in $cur_arg; do
|
||||
if [[ "$find" == "y" ]]; then
|
||||
if [[ ! $var =~ "--" ]]; then
|
||||
value=$var
|
||||
fi
|
||||
break
|
||||
fi
|
||||
if [[ "--$1" == "$var" ]] || [[ "-$1" == "$var" ]]; then
|
||||
find="y"
|
||||
value="yes"
|
||||
fi
|
||||
done
|
||||
echo $value
|
||||
}
|
||||
|
||||
is_arm() {
|
||||
local get_arch=`arch`
|
||||
if [[ $get_arch =~ "aarch" ]] || [[ $get_arch =~ "arm" ]]; then
|
||||
echo "yes"
|
||||
else
|
||||
echo "no"
|
||||
fi
|
||||
}
|
||||
|
||||
####################################################################################
|
||||
####################################################################################
|
||||
####################################################################################
|
||||
|
||||
env_init
|
||||
check_docker
|
||||
if [[ "$1" != "electron" ]]; then
|
||||
check_docker
|
||||
env_init
|
||||
fi
|
||||
|
||||
if [ $# -gt 0 ];then
|
||||
if [ $# -gt 0 ]; then
|
||||
if [[ "$1" == "init" ]] || [[ "$1" == "install" ]]; then
|
||||
shift 1
|
||||
rm -rf composer.lock
|
||||
rm -rf package-lock.json
|
||||
mkdir -p ${cur_path}/docker/mysql/data
|
||||
chmod -R 777 ${cur_path}/docker/mysql/data
|
||||
docker-compose up -d
|
||||
sleep 3
|
||||
# 判断架构
|
||||
if [[ "$(is_arm)" == "yes" ]] && [[ -z "$(arg_get force)" ]]; then
|
||||
echo -e "${Error} ${RedBG}暂不支持arm架构,强制安装请使用:./cmd install --force${Font}"
|
||||
exit 1
|
||||
fi
|
||||
# 初始化文件
|
||||
if [[ -n "$(arg_get relock)" ]]; then
|
||||
rm -rf node_modules
|
||||
rm -rf package-lock.json
|
||||
rm -rf vendor
|
||||
rm -rf composer.lock
|
||||
fi
|
||||
mkdir -p "${cur_path}/docker/log/supervisor"
|
||||
mkdir -p "${cur_path}/docker/mysql/data"
|
||||
chmod -R 775 "${cur_path}/docker/log/supervisor"
|
||||
chmod -R 775 "${cur_path}/docker/mysql/data"
|
||||
# 启动容器
|
||||
[[ "$(arg_get port)" -gt 0 ]] && env_set APP_PORT "$(arg_get port)"
|
||||
$COMPOSE up php -d
|
||||
# 安装composer依赖
|
||||
run_exec php "composer install"
|
||||
[ -z "$(env_get APP_KEY)" ] && run_exec php "php artisan key:generate"
|
||||
run_exec php "php artisan migrate --seed"
|
||||
if [ ! -f "${cur_path}/vendor/autoload.php" ]; then
|
||||
run_exec php "composer config repo.packagist composer https://packagist.phpcomposer.com"
|
||||
run_exec php "composer install"
|
||||
run_exec php "composer config --unset repos.packagist"
|
||||
fi
|
||||
if [ ! -f "${cur_path}/vendor/autoload.php" ]; then
|
||||
echo -e "${Error} ${RedBG}composer install 失败,请重试! ${Font}"
|
||||
exit 1
|
||||
fi
|
||||
[[ -z "$(env_get APP_KEY)" ]] && run_exec php "php artisan key:generate"
|
||||
run_exec php "php bin/run --mode=prod"
|
||||
docker-compose stop
|
||||
docker-compose start
|
||||
# 检查数据库
|
||||
remaining=10
|
||||
while [ ! -f "${cur_path}/docker/mysql/data/$(env_get DB_DATABASE)/db.opt" ]; do
|
||||
((remaining=$remaining-1))
|
||||
if [ $remaining -lt 0 ]; then
|
||||
echo -e "${Error} ${RedBG} 数据库安装失败! ${Font}"
|
||||
exit 1
|
||||
fi
|
||||
chmod -R 775 "${cur_path}/docker/mysql/data"
|
||||
sleep 3
|
||||
done
|
||||
run_exec php "php artisan migrate --seed"
|
||||
# 设置初始化密码
|
||||
res=`run_exec mariadb "sh /etc/mysql/repassword.sh"`
|
||||
$COMPOSE up -d
|
||||
supervisorctl_restart php
|
||||
echo -e "${OK} ${GreenBG} 安装完成 ${Font}"
|
||||
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
|
||||
echo -e "$res"
|
||||
elif [[ "$1" == "update" ]]; then
|
||||
shift 1
|
||||
run_mysql backup
|
||||
git fetch --all
|
||||
git reset --hard origin/$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')
|
||||
git pull
|
||||
run_exec php "composer update"
|
||||
run_exec php "php artisan migrate"
|
||||
supervisorctl_restart php
|
||||
docker-compose up -d
|
||||
$COMPOSE up -d
|
||||
elif [[ "$1" == "uninstall" ]]; then
|
||||
shift 1
|
||||
read -rp "确定要卸载(含:删除容器、数据库、日志)吗?(y/n): " uninstall
|
||||
@ -184,25 +333,41 @@ if [ $# -gt 0 ];then
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
docker-compose down
|
||||
docker-compose rm -fs
|
||||
$COMPOSE down
|
||||
rm -rf "./docker/mysql/data"
|
||||
rm -rf "./docker/log/supervisor"
|
||||
find "./storage/logs" -name "*.log" | xargs rm -rf
|
||||
echo -e "${OK} ${GreenBG} 卸载完成 ${Font}"
|
||||
elif [[ "$1" == "reinstall" ]]; then
|
||||
shift 1
|
||||
./cmd uninstall $@
|
||||
sleep 3
|
||||
./cmd install $@
|
||||
elif [[ "$1" == "port" ]]; then
|
||||
shift 1
|
||||
env_set APP_PORT "$1"
|
||||
$COMPOSE up -d
|
||||
echo -e "${OK} ${GreenBG} 修改成功 ${Font}"
|
||||
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
|
||||
elif [[ "$1" == "repassword" ]]; then
|
||||
shift 1
|
||||
run_exec mariadb "sh /etc/mysql/repassword.sh \"$@\""
|
||||
elif [[ "$1" == "dev" ]] || [[ "$1" == "development" ]]; then
|
||||
shift 1
|
||||
run_compile dev
|
||||
elif [[ "$1" == "prod" ]] || [[ "$1" == "production" ]]; then
|
||||
shift 1
|
||||
run_compile prod
|
||||
elif [[ "$1" == "electron" ]]; then
|
||||
shift 1
|
||||
run_electron $@
|
||||
elif [[ "$1" == "doc" ]]; then
|
||||
shift 1
|
||||
run_exec php "php app/Http/Controllers/Api/apidoc.php"
|
||||
docker run -it --rm -v ${cur_path}:/home/node/apidoc kuaifan/apidoc -i app/Http/Controllers/Api -o public/docs
|
||||
elif [[ "$1" == "debug" ]]; then
|
||||
shift 1
|
||||
if [[ "$@" == "close" ]];then
|
||||
if [[ "$@" == "close" ]]; then
|
||||
env_set APP_DEBUG "false"
|
||||
else
|
||||
env_set APP_DEBUG "true"
|
||||
@ -210,7 +375,7 @@ if [ $# -gt 0 ];then
|
||||
supervisorctl_restart php
|
||||
elif [[ "$1" == "https" ]]; then
|
||||
shift 1
|
||||
if [[ "$@" == "auto" ]];then
|
||||
if [[ "$@" == "auto" ]]; then
|
||||
env_set APP_SCHEME "auto"
|
||||
else
|
||||
env_set APP_SCHEME "true"
|
||||
@ -222,50 +387,28 @@ if [ $# -gt 0 ];then
|
||||
elif [[ "$1" == "php" ]]; then
|
||||
shift 1
|
||||
e="php $@" && run_exec php "$e"
|
||||
elif [[ "$1" == "nginx" ]]; then
|
||||
shift 1
|
||||
e="nginx $@" && run_exec nginx "$e"
|
||||
elif [[ "$1" == "redis" ]]; then
|
||||
shift 1
|
||||
e="redis $@" && run_exec redis "$e"
|
||||
elif [[ "$1" == "mysql" ]]; then
|
||||
shift 1
|
||||
if [[ "$@" == "backup" ]]; then
|
||||
# 备份数据库
|
||||
database=$(env_get DB_DATABASE)
|
||||
username=$(env_get DB_USERNAME)
|
||||
password=$(env_get DB_PASSWORD)
|
||||
mkdir -p ${cur_path}/docker/mysql/backup
|
||||
filename="${cur_path}/docker/mysql/backup/${database}_$(date "+%Y%m%d%H%M%S").sql.gz"
|
||||
run_exec mariadb "exec mysqldump --databases $database -u$username -p$password" | gzip > $filename
|
||||
judge "备份数据库"
|
||||
[ -f "$filename" ] && echo -e "备份文件:$filename"
|
||||
elif [[ "$@" == "recovery" ]];then
|
||||
# 还原数据库
|
||||
database=$(env_get DB_DATABASE)
|
||||
username=$(env_get DB_USERNAME)
|
||||
password=$(env_get DB_PASSWORD)
|
||||
mkdir -p ${cur_path}/docker/mysql/backup
|
||||
list=`ls -1 "${cur_path}/docker/mysql/backup" | grep ".sql.gz"`
|
||||
if [ -z "$list" ]; then
|
||||
echo -e "${Error} ${RedBG} 没有备份文件!${Font}"
|
||||
exit 1
|
||||
fi
|
||||
echo "$list"
|
||||
read -rp "请输入备份文件名称还原:" inputname
|
||||
filename="${cur_path}/docker/mysql/backup/${inputname}"
|
||||
if [ ! -f "$filename" ]; then
|
||||
echo -e "${Error} ${RedBG} 备份文件:${inputname} 不存在! ${Font}"
|
||||
exit 1
|
||||
fi
|
||||
container_name=`get_docker_name mariadb`
|
||||
docker cp $filename $container_name:/
|
||||
run_exec mariadb "gunzip < /$inputname | mysql -u$username -p$password $database"
|
||||
judge "还原数据库"
|
||||
if [ "$1" = "backup" ]; then
|
||||
run_mysql backup
|
||||
elif [ "$1" = "recovery" ]; then
|
||||
run_mysql recovery
|
||||
else
|
||||
e="mysql $@" && run_exec mariadb "$e"
|
||||
fi
|
||||
elif [[ "$1" == "composer" ]]; then
|
||||
shift 1
|
||||
e="composer $@" && run_exec php "$e"
|
||||
elif [[ "$1" == "super" ]]; then
|
||||
elif [[ "$1" == "service" ]]; then
|
||||
shift 1
|
||||
supervisorctl_restart "$@"
|
||||
elif [[ "$1" == "supervisorctl" ]]; then
|
||||
e="service $@" && run_exec php "$e"
|
||||
elif [[ "$1" == "super" ]] || [[ "$1" == "supervisorctl" ]]; then
|
||||
shift 1
|
||||
e="supervisorctl $@" && run_exec php "$e"
|
||||
elif [[ "$1" == "models" ]]; then
|
||||
@ -276,11 +419,11 @@ if [ $# -gt 0 ];then
|
||||
e="./vendor/bin/phpunit $@" && run_exec php "$e"
|
||||
elif [[ "$1" == "restart" ]]; then
|
||||
shift 1
|
||||
docker-compose stop "$@"
|
||||
docker-compose start "$@"
|
||||
$COMPOSE stop "$@"
|
||||
$COMPOSE start "$@"
|
||||
else
|
||||
docker-compose "$@"
|
||||
$COMPOSE "$@"
|
||||
fi
|
||||
else
|
||||
docker-compose ps
|
||||
$COMPOSE ps
|
||||
fi
|
||||
|
9554
composer.lock
generated
Normal file
9554
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ class CreateProjectLogsTable extends Migration
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('project_id')->nullable()->default(0)->comment('项目ID');
|
||||
$table->bigInteger('column_id')->nullable()->default(0)->comment('列表ID');
|
||||
$table->bigInteger('task_id')->nullable()->default(0)->comment('项目ID');
|
||||
$table->bigInteger('task_id')->nullable()->default(0)->comment('任务ID');
|
||||
$table->bigInteger('userid')->nullable()->default(0)->comment('会员ID');
|
||||
$table->string('detail', 500)->nullable()->default('')->comment('详细信息');
|
||||
$table->timestamps();
|
||||
|
47
database/migrations/2021_12_10_170751_files_add_ext.php
Normal file
47
database/migrations/2021_12_10_170751_files_add_ext.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class FilesAddExt extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$isAdd = false;
|
||||
Schema::table('files', function (Blueprint $table) use (&$isAdd) {
|
||||
if (!Schema::hasColumn('files', 'ext')) {
|
||||
$isAdd = true;
|
||||
$table->string('ext', 20)->nullable()->default('')->after('type')->comment('后缀名');
|
||||
}
|
||||
});
|
||||
if ($isAdd) {
|
||||
// 更新数据
|
||||
\App\Models\File::chunkById(100, function ($lists) {
|
||||
foreach ($lists as $item) {
|
||||
if (in_array($item->type, ['word', 'excel', 'ppt'])) {
|
||||
$item->ext = str_replace(['word', 'excel', 'ppt'], ['docx', 'xlsx', 'pptx'], $item->type);
|
||||
$item->save();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('files', function (Blueprint $table) {
|
||||
$table->dropColumn("ext");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
class ProjectTasksAddArchivedFollow extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$isAdd = false;
|
||||
Schema::table('project_tasks', function (Blueprint $table) use (&$isAdd) {
|
||||
if (!Schema::hasColumn('project_tasks', 'archived_follow')) {
|
||||
$isAdd = true;
|
||||
$table->tinyInteger('archived_follow')->nullable()->default(0)->after('archived_userid')->comment('跟随项目归档(项目取消归档时任务也取消归档)');
|
||||
}
|
||||
});
|
||||
if ($isAdd) {
|
||||
// 更新数据
|
||||
Project::whereNotNull('archived_at')->chunkById(100, function ($lists) {
|
||||
foreach ($lists as $item) {
|
||||
ProjectTask::whereProjectId($item->id)->whereArchivedAt(null)->update([
|
||||
'archived_at' => $item->archived_at,
|
||||
'archived_follow' => 1
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('project_tasks', function (Blueprint $table) {
|
||||
$table->dropColumn("archived_follow");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProjectInvitesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('project_invites', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('project_id')->nullable()->default(0)->comment('项目ID');
|
||||
$table->integer('num')->nullable()->default(0)->comment('累计邀请');
|
||||
$table->string('code')->nullable()->default('')->comment('链接码');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('project_invites');
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateFileLinksTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('file_links', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('file_id')->nullable()->default(0)->comment('项目ID');
|
||||
$table->integer('num')->nullable()->default(0)->comment('累计访问');
|
||||
$table->string('code')->nullable()->default('')->comment('链接码');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('file_links');
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\FileUser;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class FileUsersAddPermission extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$isAdd = false;
|
||||
Schema::table('file_users', function (Blueprint $table) use (&$isAdd) {
|
||||
if (!Schema::hasColumn('file_users', 'permission')) {
|
||||
$isAdd = true;
|
||||
$table->tinyInteger('permission')->nullable()->default(0)->after('userid')->comment('权限:0只读,1读写');
|
||||
}
|
||||
});
|
||||
if ($isAdd) {
|
||||
// 更新数据
|
||||
File::whereShare(1)->chunkById(100, function ($lists) {
|
||||
foreach ($lists as $file) {
|
||||
FileUser::updateInsert([
|
||||
'file_id' => $file->id,
|
||||
'userid' => 0,
|
||||
]);
|
||||
}
|
||||
});
|
||||
File::whereShare(2)->update([
|
||||
'share' => 1,
|
||||
]);
|
||||
FileUser::wherePermission(0)->update([
|
||||
'permission' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('file_users', function (Blueprint $table) {
|
||||
$table->dropColumn("permission");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateReportsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ( Schema::hasTable('reports') )
|
||||
return;
|
||||
|
||||
Schema::create('reports', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->timestamps();
|
||||
$table->string("title")->default("")->comment("标题");
|
||||
$table->enum("type", ["weekly", "daily"])->default("daily")->comment("汇报类型");
|
||||
$table->unsignedBigInteger("userid")->default(0);
|
||||
$table->longText("content")->nullable();
|
||||
$table->index(["userid", "created_at"], "default");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('reports');
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateReportReceivesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ( Schema::hasTable('report_receives') )
|
||||
return;
|
||||
|
||||
Schema::create('report_receives', function (Blueprint $table) {
|
||||
$table->bigIncrements("id");
|
||||
$table->unsignedInteger("rid")->default(0);
|
||||
$table->timestamp("receive_time")->nullable()->comment("接收时间");
|
||||
$table->unsignedBigInteger("userid")->default(0)->comment("接收人");
|
||||
$table->unsignedTinyInteger("read")->default(0)->comment("是否已读");
|
||||
$table->index(["userid", "receive_time"], "default");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('report_receives');
|
||||
}
|
||||
}
|
32
database/migrations/2022_01_04_111739_add_report_sign.php
Normal file
32
database/migrations/2022_01_04_111739_add_report_sign.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddReportSign extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('reports', function (Blueprint $table) {
|
||||
$table->string("sign")->default("")->comment("汇报唯一标识");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('reports', function (Blueprint $table) {
|
||||
$table->dropColumn("sign");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UsersAddDisableAt extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$isAdd = false;
|
||||
Schema::table('users', function (Blueprint $table) use (&$isAdd) {
|
||||
if (!Schema::hasColumn('users', 'disable_at')) {
|
||||
$isAdd = true;
|
||||
$table->timestamp('disable_at')->nullable()->after('created_ip')->comment('禁用时间');
|
||||
}
|
||||
});
|
||||
if ($isAdd) {
|
||||
User::where("identity", "like", "%,disable,%")->update([
|
||||
'disable_at' => Carbon::now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn("disable_at");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use App\Models\ProjectTask;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class ProjectTasksUpdateSubtaskTime extends Migration
|
||||
{
|
||||
/**
|
||||
* 子任务同步主任务(任务时间)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
ProjectTask::where('parent_id', '>', 0)
|
||||
->whereNull('end_at')
|
||||
->chunkById(100, function ($lists) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($lists as $task) {
|
||||
$parent = ProjectTask::whereNotNull('end_at')->find($task->parent_id);
|
||||
if ($parent) {
|
||||
$task->start_at = $parent->start_at;
|
||||
$task->end_at = $parent->end_at;
|
||||
$task->save();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
use App\Models\ProjectTask;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class ProjectTasksUpdateSubtaskArchivedDelete extends Migration
|
||||
{
|
||||
/**
|
||||
* 子任务同步主任务(归档、删除)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// 归档
|
||||
ProjectTask::whereParentId(0)
|
||||
->whereNotNull('archived_at')
|
||||
->chunkById(100, function ($lists) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($lists as $task) {
|
||||
ProjectTask::whereParentId($task->id)->update([
|
||||
'archived_at' => $task->archived_at,
|
||||
'archived_userid' => $task->archived_userid,
|
||||
'archived_follow' => $task->archived_follow,
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
// 删除
|
||||
ProjectTask::onlyTrashed()
|
||||
->whereParentId(0)
|
||||
->chunkById(100, function ($lists) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($lists as $task) {
|
||||
ProjectTask::whereParentId($task->id)->update([
|
||||
'deleted_at' => $task->deleted_at,
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProjectFlowItemsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('project_flow_items', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('project_id')->nullable()->default(0)->comment('项目ID');
|
||||
$table->bigInteger('flow_id')->nullable()->default(0)->comment('流程ID');
|
||||
$table->string('name', 50)->nullable()->default('')->comment('名称');
|
||||
$table->string('status', 20)->nullable()->default('')->comment('状态');
|
||||
$table->string('turns')->nullable()->default('')->comment('可流转');
|
||||
$table->string('userids')->nullable()->default('')->comment('状态负责人ID');
|
||||
$table->integer('sort')->nullable()->comment('排序');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('project_flow_items');
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProjectFlowsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('project_flows', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('project_id')->nullable()->default(0)->comment('项目ID');
|
||||
$table->string('name', 50)->nullable()->default('')->comment('流程名称');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('project_flows');
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ProjectTasksAddFlowItemIdFlowItemName extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('project_tasks', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('project_tasks', 'flow_item_id')) {
|
||||
$table->bigInteger('flow_item_id')->nullable()->default(0)->after('dialog_id')->comment('工作流状态ID');
|
||||
$table->string('flow_item_name', 50)->nullable()->default('')->after('flow_item_id')->comment('工作流状态名称');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('project_tasks', function (Blueprint $table) {
|
||||
$table->dropColumn("flow_item_id");
|
||||
$table->dropColumn("flow_item_name");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use App\Models\ProjectFlowItem;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ProjectFlowItemsAddUsertype extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$isAdd = false;
|
||||
Schema::table('project_flow_items', function (Blueprint $table) use (&$isAdd) {
|
||||
if (!Schema::hasColumn('project_flow_items', 'usertype')) {
|
||||
$isAdd = true;
|
||||
$table->string('usertype', 10)->nullable()->default('')->after('userids')->comment('流转模式');
|
||||
}
|
||||
});
|
||||
if ($isAdd) {
|
||||
ProjectFlowItem::where("usertype", "")->update([
|
||||
'usertype' => 'add',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('project_flow_items', function (Blueprint $table) {
|
||||
$table->dropColumn("usertype");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ProjectLogsAddRecord extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('project_logs', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('project_logs', 'record')) {
|
||||
$table->text('record')->nullable()->after('detail')->comment('记录数据');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('project_logs', function (Blueprint $table) {
|
||||
$table->dropColumn("record");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ProjectFlowItemsAddUserlimit extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('project_flow_items', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('project_flow_items', 'userlimit')) {
|
||||
$table->tinyInteger('userlimit')->nullable()->default(0)->after('usertype')->comment('限制负责人');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('project_flow_items', function (Blueprint $table) {
|
||||
$table->dropColumn("userlimit");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class WebSocketDialogMsgsAddDeletes 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', 'deleted_at')) {
|
||||
$table->softDeletes();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class InsertSettingColumnTemplate extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$array = \App\Module\Base::setting('columnTemplate');
|
||||
if (empty($array)) {
|
||||
\App\Module\Base::setting('columnTemplate', [
|
||||
[
|
||||
'name' => '软件开发',
|
||||
'columns' => ['产品规划', '前端开发', '后端开发', '测试', '发布', '其他'],
|
||||
],
|
||||
[
|
||||
'name' => '产品开发',
|
||||
'columns' => ['产品计划', '正在设计', '正在研发', '测试', '准备发布', '发布成功'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class WebSocketDialogUsersAddTopAt extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('web_socket_dialog_users', 'top_at')) {
|
||||
$table->timestamp('top_at')->nullable()->after('userid')->comment('置顶时间');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
|
||||
$table->dropColumn("top_at");
|
||||
});
|
||||
}
|
||||
}
|
30
database/migrations/2022_02_20_171030_files_update_type.php
Normal file
30
database/migrations/2022_02_20_171030_files_update_type.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use App\Models\File;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class FilesUpdateType extends Migration
|
||||
{
|
||||
/**
|
||||
* 更改流程图文件类型
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
File::whereType('flow')->update([
|
||||
'type' => 'drawio'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
File::whereType('drawio')->update([
|
||||
'type' => 'flow'
|
||||
]);
|
||||
}
|
||||
}
|
75
database/migrations/2022_02_21_203230_files_update_ext.php
Normal file
75
database/migrations/2022_02_21_203230_files_update_ext.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\FileContent;
|
||||
use App\Module\Base;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class FilesUpdateExt extends Migration
|
||||
{
|
||||
/**
|
||||
* 更新后缀
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
File::whereIn('type', ['mind', 'drawio', 'document'])->where('ext', '')->orderBy('id')->chunk(100, function($files) {
|
||||
/** @var File $file */
|
||||
foreach ($files as $file) {
|
||||
$fileContent = FileContent::whereFid($file->id)->orderByDesc('id')->first();
|
||||
$contentArray = Base::json2array($fileContent?->content);
|
||||
$contentString = '';
|
||||
//
|
||||
switch ($file->type) {
|
||||
case 'document':
|
||||
$file->ext = $contentArray['type'] ?: 'md';
|
||||
$contentString = $contentArray['content'];
|
||||
break;
|
||||
case 'drawio':
|
||||
$file->ext = 'drawio';
|
||||
$contentString = $contentArray['xml'];
|
||||
break;
|
||||
case 'mind':
|
||||
$file->ext = 'mind';
|
||||
$contentString = $fileContent?->content;
|
||||
break;
|
||||
}
|
||||
$file->save();
|
||||
//
|
||||
$path = 'uploads/file/' . $file->type . '/' . date("Ym", Carbon::parse($file->created_at)->timestamp) . '/' . $file->id . '/' . md5($contentString);
|
||||
$save = public_path($path);
|
||||
Base::makeDir(dirname($save));
|
||||
file_put_contents($save, $contentString);
|
||||
$content = [
|
||||
'type' => $file->ext,
|
||||
'url' => $path
|
||||
];
|
||||
//
|
||||
$content = FileContent::createInstance([
|
||||
'fid' => $file->id,
|
||||
'content' => $content,
|
||||
'text' => $fileContent?->text,
|
||||
'size' => $file->size,
|
||||
'userid' => $file->userid,
|
||||
]);
|
||||
$content->save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
File::whereIn('ext', ['mind', 'drawio', 'md'])->update([
|
||||
'ext' => ''
|
||||
]);
|
||||
// ... 退回去意义不大,文件内容不做回滚操作
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ProjectUsersAddTopAt extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('project_users', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('project_users', 'top_at')) {
|
||||
$table->timestamp('top_at')->nullable()->after('owner')->comment('置顶时间');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('project_users', function (Blueprint $table) {
|
||||
$table->dropColumn("top_at");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProjectTaskFlowChangesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('project_task_flow_changes', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('task_id')->nullable()->default(0)->comment('任务ID');
|
||||
$table->bigInteger('userid')->nullable()->default(0)->comment('会员ID');
|
||||
$table->bigInteger('before_item_id')->nullable()->default(0)->comment('(变化前)工作流状态ID');
|
||||
$table->string('before_item_name', 50)->nullable()->default('')->comment('(变化前)工作流状态名称');
|
||||
$table->bigInteger('after_item_id')->nullable()->default(0)->comment('(变化后)工作流状态ID');
|
||||
$table->string('after_item_name', 50)->nullable()->default('')->comment('(变化后)工作流状态名称');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('project_task_flow_changes');
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class RenamePreProjectTaskFlowChangesItem extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('project_task_flow_changes', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('project_task_flow_changes', 'before_item_id')) {
|
||||
$table->renameColumn('before_item_id', 'before_flow_item_id');
|
||||
$table->renameColumn('before_item_name', 'before_flow_item_name');
|
||||
$table->renameColumn('after_item_id', 'after_flow_item_id');
|
||||
$table->renameColumn('after_item_name', 'after_flow_item_name');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('project_task_flow_changes', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
@ -16,7 +16,10 @@ class DatabaseSeeder extends Seeder
|
||||
|
||||
$this->call(FileContentsTableSeeder::class);
|
||||
$this->call(FilesTableSeeder::class);
|
||||
$this->call(FileUsersTableSeeder::class);
|
||||
$this->call(ProjectColumnsTableSeeder::class);
|
||||
$this->call(ProjectFlowItemsTableSeeder::class);
|
||||
$this->call(ProjectFlowsTableSeeder::class);
|
||||
$this->call(ProjectLogsTableSeeder::class);
|
||||
$this->call(ProjectTaskContentsTableSeeder::class);
|
||||
$this->call(ProjectTaskUsersTableSeeder::class);
|
||||
|
@ -599,7 +599,5 @@ curl -O https://task.hitosea.com/uploads/files/3/202105/ba786dfc2f4c2fe916880474
|
||||
'deleted_at' => NULL,
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
82
database/seeders/FileUsersTableSeeder.php
Normal file
82
database/seeders/FileUsersTableSeeder.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class FileUsersTableSeeder extends Seeder
|
||||
{
|
||||
|
||||
/**
|
||||
* Auto generated seed file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
|
||||
|
||||
if (\DB::table('file_users')->count() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
\DB::table('file_users')->insert(array (
|
||||
0 =>
|
||||
array (
|
||||
'id' => 1,
|
||||
'file_id' => 1,
|
||||
'userid' => 0,
|
||||
'permission' => 1,
|
||||
'created_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
'updated_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'id' => 2,
|
||||
'file_id' => 11,
|
||||
'userid' => 0,
|
||||
'permission' => 1,
|
||||
'created_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
'updated_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
),
|
||||
2 =>
|
||||
array (
|
||||
'id' => 3,
|
||||
'file_id' => 12,
|
||||
'userid' => 0,
|
||||
'permission' => 1,
|
||||
'created_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
'updated_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
),
|
||||
3 =>
|
||||
array (
|
||||
'id' => 4,
|
||||
'file_id' => 13,
|
||||
'userid' => 0,
|
||||
'permission' => 1,
|
||||
'created_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
'updated_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
),
|
||||
4 =>
|
||||
array (
|
||||
'id' => 5,
|
||||
'file_id' => 15,
|
||||
'userid' => 0,
|
||||
'permission' => 1,
|
||||
'created_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
'updated_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
),
|
||||
5 =>
|
||||
array (
|
||||
'id' => 6,
|
||||
'file_id' => 16,
|
||||
'userid' => 0,
|
||||
'permission' => 1,
|
||||
'created_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
'updated_at' => seeders_at('2021-07-01 14:03:29'),
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -2,6 +2,10 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\FileContent;
|
||||
use App\Module\Base;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class FilesTableSeeder extends Seeder
|
||||
@ -28,6 +32,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '设计知识',
|
||||
'type' => 'folder',
|
||||
'ext' => '',
|
||||
'size' => 0,
|
||||
'userid' => 1,
|
||||
'share' => 1,
|
||||
@ -43,6 +48,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '如何搭建B端设计规范?',
|
||||
'type' => 'document',
|
||||
'ext' => '',
|
||||
'size' => 16976,
|
||||
'userid' => 1,
|
||||
'share' => 0,
|
||||
@ -58,6 +64,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '页面设计中的信息组织策略探索-言之有序',
|
||||
'type' => 'document',
|
||||
'ext' => '',
|
||||
'size' => 11971,
|
||||
'userid' => 1,
|
||||
'share' => 0,
|
||||
@ -73,6 +80,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '素材整理',
|
||||
'type' => 'folder',
|
||||
'ext' => '',
|
||||
'size' => 0,
|
||||
'userid' => 1,
|
||||
'share' => 0,
|
||||
@ -88,6 +96,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '配置静态IP地址',
|
||||
'type' => 'document',
|
||||
'ext' => '',
|
||||
'size' => 285,
|
||||
'userid' => 1,
|
||||
'share' => 0,
|
||||
@ -103,6 +112,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '脑图',
|
||||
'type' => 'mind',
|
||||
'ext' => '',
|
||||
'size' => 1947,
|
||||
'userid' => 1,
|
||||
'share' => 0,
|
||||
@ -118,6 +128,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '数据统计',
|
||||
'type' => 'excel',
|
||||
'ext' => 'xlsx',
|
||||
'size' => 8128,
|
||||
'userid' => 1,
|
||||
'share' => 0,
|
||||
@ -133,6 +144,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '会议纪要',
|
||||
'type' => 'document',
|
||||
'ext' => '',
|
||||
'size' => 8088,
|
||||
'userid' => 1,
|
||||
'share' => 0,
|
||||
@ -148,6 +160,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '部门周报',
|
||||
'type' => 'document',
|
||||
'ext' => '',
|
||||
'size' => 23266,
|
||||
'userid' => 1,
|
||||
'share' => 0,
|
||||
@ -163,6 +176,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '项目管理',
|
||||
'type' => 'excel',
|
||||
'ext' => 'xlsx',
|
||||
'size' => 8128,
|
||||
'userid' => 1,
|
||||
'share' => 0,
|
||||
@ -178,6 +192,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '工作计划',
|
||||
'type' => 'excel',
|
||||
'ext' => 'xlsx',
|
||||
'size' => 8128,
|
||||
'userid' => 1,
|
||||
'share' => 1,
|
||||
@ -192,7 +207,8 @@ class FilesTableSeeder extends Seeder
|
||||
'pid' => 0,
|
||||
'cid' => 0,
|
||||
'name' => '流程图',
|
||||
'type' => 'flow',
|
||||
'type' => 'drawio',
|
||||
'ext' => '',
|
||||
'size' => 5418,
|
||||
'userid' => 1,
|
||||
'share' => 1,
|
||||
@ -208,6 +224,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '项目管理',
|
||||
'type' => 'folder',
|
||||
'ext' => '',
|
||||
'size' => 0,
|
||||
'userid' => 1,
|
||||
'share' => 1,
|
||||
@ -223,6 +240,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '会议纪要',
|
||||
'type' => 'folder',
|
||||
'ext' => '',
|
||||
'size' => 0,
|
||||
'userid' => 1,
|
||||
'share' => 0,
|
||||
@ -238,6 +256,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '会议发言',
|
||||
'type' => 'word',
|
||||
'ext' => 'docx',
|
||||
'size' => 10994,
|
||||
'userid' => 1,
|
||||
'share' => 1,
|
||||
@ -253,6 +272,7 @@ class FilesTableSeeder extends Seeder
|
||||
'cid' => 0,
|
||||
'name' => '产品介绍',
|
||||
'type' => 'ppt',
|
||||
'ext' => 'pptx',
|
||||
'size' => 26882,
|
||||
'userid' => 1,
|
||||
'share' => 1,
|
||||
@ -264,5 +284,47 @@ class FilesTableSeeder extends Seeder
|
||||
));
|
||||
|
||||
|
||||
File::whereIn('type', ['mind', 'drawio', 'document'])->where('ext', '')->orderBy('id')->chunk(100, function($files) {
|
||||
/** @var File $file */
|
||||
foreach ($files as $file) {
|
||||
$fileContent = FileContent::whereFid($file->id)->orderByDesc('id')->first();
|
||||
$contentArray = Base::json2array($fileContent?->content);
|
||||
$contentString = '';
|
||||
//
|
||||
switch ($file->type) {
|
||||
case 'document':
|
||||
$file->ext = $contentArray['type'] ?: 'md';
|
||||
$contentString = $contentArray['content'];
|
||||
break;
|
||||
case 'drawio':
|
||||
$file->ext = 'drawio';
|
||||
$contentString = $contentArray['xml'];
|
||||
break;
|
||||
case 'mind':
|
||||
$file->ext = 'mind';
|
||||
$contentString = $fileContent?->content;
|
||||
break;
|
||||
}
|
||||
$file->save();
|
||||
//
|
||||
$path = 'uploads/file/' . $file->type . '/' . date("Ym", Carbon::parse($file->created_at)->timestamp) . '/' . $file->id . '/' . md5($contentString);
|
||||
$save = public_path($path);
|
||||
Base::makeDir(dirname($save));
|
||||
file_put_contents($save, $contentString);
|
||||
$content = [
|
||||
'type' => $file->ext,
|
||||
'url' => $path
|
||||
];
|
||||
//
|
||||
$content = FileContent::createInstance([
|
||||
'fid' => $file->id,
|
||||
'content' => $content,
|
||||
'text' => $fileContent?->text,
|
||||
'size' => $file->size,
|
||||
'userid' => $file->userid,
|
||||
]);
|
||||
$content->save();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
388
database/seeders/ProjectFlowItemsTableSeeder.php
Normal file
388
database/seeders/ProjectFlowItemsTableSeeder.php
Normal file
@ -0,0 +1,388 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class ProjectFlowItemsTableSeeder extends Seeder
|
||||
{
|
||||
|
||||
/**
|
||||
* Auto generated seed file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
|
||||
|
||||
if (\DB::table('project_flow_items')->count() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
\DB::table('project_flow_items')->insert(array (
|
||||
0 =>
|
||||
array (
|
||||
'id' => 1,
|
||||
'project_id' => 2,
|
||||
'flow_id' => 1,
|
||||
'name' => '待处理',
|
||||
'status' => 'start',
|
||||
'turns' => '[1,2,3,4]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 0,
|
||||
'created_at' => '2022-01-15 23:43:15',
|
||||
'updated_at' => '2022-01-15 23:43:15',
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'id' => 2,
|
||||
'project_id' => 2,
|
||||
'flow_id' => 1,
|
||||
'name' => '进行中',
|
||||
'status' => 'progress',
|
||||
'turns' => '[1,2,3,4]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 1,
|
||||
'created_at' => '2022-01-15 23:43:15',
|
||||
'updated_at' => '2022-01-15 23:43:15',
|
||||
),
|
||||
2 =>
|
||||
array (
|
||||
'id' => 3,
|
||||
'project_id' => 2,
|
||||
'flow_id' => 1,
|
||||
'name' => '已完成',
|
||||
'status' => 'end',
|
||||
'turns' => '[1,2,3,4]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 2,
|
||||
'created_at' => '2022-01-15 23:43:15',
|
||||
'updated_at' => '2022-01-15 23:43:15',
|
||||
),
|
||||
3 =>
|
||||
array (
|
||||
'id' => 4,
|
||||
'project_id' => 2,
|
||||
'flow_id' => 1,
|
||||
'name' => '已取消',
|
||||
'status' => 'end',
|
||||
'turns' => '[1,2,3,4]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 3,
|
||||
'created_at' => '2022-01-15 23:43:15',
|
||||
'updated_at' => '2022-01-15 23:43:15',
|
||||
),
|
||||
4 =>
|
||||
array (
|
||||
'id' => 5,
|
||||
'project_id' => 3,
|
||||
'flow_id' => 2,
|
||||
'name' => '待处理',
|
||||
'status' => 'start',
|
||||
'turns' => '[5,6,7,8]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 0,
|
||||
'created_at' => '2022-01-15 23:43:23',
|
||||
'updated_at' => '2022-01-15 23:43:23',
|
||||
),
|
||||
5 =>
|
||||
array (
|
||||
'id' => 6,
|
||||
'project_id' => 3,
|
||||
'flow_id' => 2,
|
||||
'name' => '进行中',
|
||||
'status' => 'progress',
|
||||
'turns' => '[5,6,7,8]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 1,
|
||||
'created_at' => '2022-01-15 23:43:23',
|
||||
'updated_at' => '2022-01-15 23:43:23',
|
||||
),
|
||||
6 =>
|
||||
array (
|
||||
'id' => 7,
|
||||
'project_id' => 3,
|
||||
'flow_id' => 2,
|
||||
'name' => '已完成',
|
||||
'status' => 'end',
|
||||
'turns' => '[5,6,7,8]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 2,
|
||||
'created_at' => '2022-01-15 23:43:23',
|
||||
'updated_at' => '2022-01-15 23:43:23',
|
||||
),
|
||||
7 =>
|
||||
array (
|
||||
'id' => 8,
|
||||
'project_id' => 3,
|
||||
'flow_id' => 2,
|
||||
'name' => '已取消',
|
||||
'status' => 'end',
|
||||
'turns' => '[5,6,7,8]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 3,
|
||||
'created_at' => '2022-01-15 23:43:23',
|
||||
'updated_at' => '2022-01-15 23:43:23',
|
||||
),
|
||||
8 =>
|
||||
array (
|
||||
'id' => 9,
|
||||
'project_id' => 4,
|
||||
'flow_id' => 3,
|
||||
'name' => '待处理',
|
||||
'status' => 'start',
|
||||
'turns' => '[9,10,11,12]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 0,
|
||||
'created_at' => '2022-01-15 23:43:28',
|
||||
'updated_at' => '2022-01-15 23:43:28',
|
||||
),
|
||||
9 =>
|
||||
array (
|
||||
'id' => 10,
|
||||
'project_id' => 4,
|
||||
'flow_id' => 3,
|
||||
'name' => '进行中',
|
||||
'status' => 'progress',
|
||||
'turns' => '[9,10,11,12]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 1,
|
||||
'created_at' => '2022-01-15 23:43:28',
|
||||
'updated_at' => '2022-01-15 23:43:28',
|
||||
),
|
||||
10 =>
|
||||
array (
|
||||
'id' => 11,
|
||||
'project_id' => 4,
|
||||
'flow_id' => 3,
|
||||
'name' => '已完成',
|
||||
'status' => 'end',
|
||||
'turns' => '[9,10,11,12]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 2,
|
||||
'created_at' => '2022-01-15 23:43:28',
|
||||
'updated_at' => '2022-01-15 23:43:28',
|
||||
),
|
||||
11 =>
|
||||
array (
|
||||
'id' => 12,
|
||||
'project_id' => 4,
|
||||
'flow_id' => 3,
|
||||
'name' => '已取消',
|
||||
'status' => 'end',
|
||||
'turns' => '[9,10,11,12]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 3,
|
||||
'created_at' => '2022-01-15 23:43:28',
|
||||
'updated_at' => '2022-01-15 23:43:28',
|
||||
),
|
||||
12 =>
|
||||
array (
|
||||
'id' => 13,
|
||||
'project_id' => 5,
|
||||
'flow_id' => 4,
|
||||
'name' => '待处理',
|
||||
'status' => 'start',
|
||||
'turns' => '[13,14,15,16]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 0,
|
||||
'created_at' => '2022-01-15 23:43:34',
|
||||
'updated_at' => '2022-01-15 23:43:34',
|
||||
),
|
||||
13 =>
|
||||
array (
|
||||
'id' => 14,
|
||||
'project_id' => 5,
|
||||
'flow_id' => 4,
|
||||
'name' => '进行中',
|
||||
'status' => 'progress',
|
||||
'turns' => '[13,14,15,16]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 1,
|
||||
'created_at' => '2022-01-15 23:43:34',
|
||||
'updated_at' => '2022-01-15 23:43:34',
|
||||
),
|
||||
14 =>
|
||||
array (
|
||||
'id' => 15,
|
||||
'project_id' => 5,
|
||||
'flow_id' => 4,
|
||||
'name' => '已完成',
|
||||
'status' => 'end',
|
||||
'turns' => '[13,14,15,16]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 2,
|
||||
'created_at' => '2022-01-15 23:43:34',
|
||||
'updated_at' => '2022-01-15 23:43:34',
|
||||
),
|
||||
15 =>
|
||||
array (
|
||||
'id' => 16,
|
||||
'project_id' => 5,
|
||||
'flow_id' => 4,
|
||||
'name' => '已取消',
|
||||
'status' => 'end',
|
||||
'turns' => '[13,14,15,16]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 3,
|
||||
'created_at' => '2022-01-15 23:43:34',
|
||||
'updated_at' => '2022-01-15 23:43:34',
|
||||
),
|
||||
16 =>
|
||||
array (
|
||||
'id' => 17,
|
||||
'project_id' => 6,
|
||||
'flow_id' => 5,
|
||||
'name' => '待处理',
|
||||
'status' => 'start',
|
||||
'turns' => '[17,18,19,20]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 0,
|
||||
'created_at' => '2022-01-15 23:43:40',
|
||||
'updated_at' => '2022-01-15 23:43:40',
|
||||
),
|
||||
17 =>
|
||||
array (
|
||||
'id' => 18,
|
||||
'project_id' => 6,
|
||||
'flow_id' => 5,
|
||||
'name' => '进行中',
|
||||
'status' => 'progress',
|
||||
'turns' => '[17,18,19,20]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 1,
|
||||
'created_at' => '2022-01-15 23:43:40',
|
||||
'updated_at' => '2022-01-15 23:43:40',
|
||||
),
|
||||
18 =>
|
||||
array (
|
||||
'id' => 19,
|
||||
'project_id' => 6,
|
||||
'flow_id' => 5,
|
||||
'name' => '已完成',
|
||||
'status' => 'end',
|
||||
'turns' => '[17,18,19,20]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 2,
|
||||
'created_at' => '2022-01-15 23:43:40',
|
||||
'updated_at' => '2022-01-15 23:43:40',
|
||||
),
|
||||
19 =>
|
||||
array (
|
||||
'id' => 20,
|
||||
'project_id' => 6,
|
||||
'flow_id' => 5,
|
||||
'name' => '已取消',
|
||||
'status' => 'end',
|
||||
'turns' => '[17,18,19,20]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 3,
|
||||
'created_at' => '2022-01-15 23:43:40',
|
||||
'updated_at' => '2022-01-15 23:43:40',
|
||||
),
|
||||
20 =>
|
||||
array (
|
||||
'id' => 21,
|
||||
'project_id' => 7,
|
||||
'flow_id' => 6,
|
||||
'name' => '待处理',
|
||||
'status' => 'start',
|
||||
'turns' => '[21,22,23,24]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 0,
|
||||
'created_at' => '2022-01-15 23:43:45',
|
||||
'updated_at' => '2022-01-15 23:43:45',
|
||||
),
|
||||
21 =>
|
||||
array (
|
||||
'id' => 22,
|
||||
'project_id' => 7,
|
||||
'flow_id' => 6,
|
||||
'name' => '进行中',
|
||||
'status' => 'progress',
|
||||
'turns' => '[21,22,23,24]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 1,
|
||||
'created_at' => '2022-01-15 23:43:45',
|
||||
'updated_at' => '2022-01-15 23:43:45',
|
||||
),
|
||||
22 =>
|
||||
array (
|
||||
'id' => 23,
|
||||
'project_id' => 7,
|
||||
'flow_id' => 6,
|
||||
'name' => '已完成',
|
||||
'status' => 'end',
|
||||
'turns' => '[21,22,23,24]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 2,
|
||||
'created_at' => '2022-01-15 23:43:45',
|
||||
'updated_at' => '2022-01-15 23:43:45',
|
||||
),
|
||||
23 =>
|
||||
array (
|
||||
'id' => 24,
|
||||
'project_id' => 7,
|
||||
'flow_id' => 6,
|
||||
'name' => '已取消',
|
||||
'status' => 'end',
|
||||
'turns' => '[21,22,23,24]',
|
||||
'userids' => '[]',
|
||||
'usertype' => 'add',
|
||||
'userlimit' => 0,
|
||||
'sort' => 3,
|
||||
'created_at' => '2022-01-15 23:43:45',
|
||||
'updated_at' => '2022-01-15 23:43:45',
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
}
|
||||
}
|
76
database/seeders/ProjectFlowsTableSeeder.php
Normal file
76
database/seeders/ProjectFlowsTableSeeder.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class ProjectFlowsTableSeeder extends Seeder
|
||||
{
|
||||
|
||||
/**
|
||||
* Auto generated seed file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
|
||||
|
||||
if (\DB::table('project_flows')->count() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
\DB::table('project_flows')->insert(array (
|
||||
0 =>
|
||||
array (
|
||||
'id' => 1,
|
||||
'project_id' => 2,
|
||||
'name' => 'Default',
|
||||
'created_at' => '2022-01-15 23:43:15',
|
||||
'updated_at' => '2022-01-15 23:43:15',
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'id' => 2,
|
||||
'project_id' => 3,
|
||||
'name' => 'Default',
|
||||
'created_at' => '2022-01-15 23:43:23',
|
||||
'updated_at' => '2022-01-15 23:43:23',
|
||||
),
|
||||
2 =>
|
||||
array (
|
||||
'id' => 3,
|
||||
'project_id' => 4,
|
||||
'name' => 'Default',
|
||||
'created_at' => '2022-01-15 23:43:28',
|
||||
'updated_at' => '2022-01-15 23:43:28',
|
||||
),
|
||||
3 =>
|
||||
array (
|
||||
'id' => 4,
|
||||
'project_id' => 5,
|
||||
'name' => 'Default',
|
||||
'created_at' => '2022-01-15 23:43:34',
|
||||
'updated_at' => '2022-01-15 23:43:34',
|
||||
),
|
||||
4 =>
|
||||
array (
|
||||
'id' => 5,
|
||||
'project_id' => 6,
|
||||
'name' => 'Default',
|
||||
'created_at' => '2022-01-15 23:43:40',
|
||||
'updated_at' => '2022-01-15 23:43:40',
|
||||
),
|
||||
5 =>
|
||||
array (
|
||||
'id' => 6,
|
||||
'project_id' => 7,
|
||||
'name' => 'Default',
|
||||
'created_at' => '2022-01-15 23:43:45',
|
||||
'updated_at' => '2022-01-15 23:43:45',
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -296,7 +296,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 6,
|
||||
'task_id' => 8,
|
||||
'userid' => 1,
|
||||
'detail' => '任务标记已完成:官网项目一期',
|
||||
'detail' => '标记任务已完成:官网项目一期',
|
||||
'created_at' => seeders_at('2021-07-01 10:53:47'),
|
||||
'updated_at' => seeders_at('2021-07-01 10:53:47'),
|
||||
),
|
||||
@ -2998,7 +2998,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 27,
|
||||
'task_id' => 77,
|
||||
'userid' => 1,
|
||||
'detail' => '任务标记已完成:🚴 里程碑 1 需求评审完成,资源准备到位',
|
||||
'detail' => '标记任务已完成:🚴 里程碑 1 需求评审完成,资源准备到位',
|
||||
'created_at' => seeders_at('2021-07-01 15:39:46'),
|
||||
'updated_at' => seeders_at('2021-07-01 15:39:46'),
|
||||
),
|
||||
@ -3229,7 +3229,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 32,
|
||||
'task_id' => 85,
|
||||
'userid' => 1,
|
||||
'detail' => '子任务标记已完成:首页',
|
||||
'detail' => '子标记任务已完成:首页',
|
||||
'created_at' => seeders_at('2021-07-01 16:17:30'),
|
||||
'updated_at' => seeders_at('2021-07-01 16:17:30'),
|
||||
),
|
||||
@ -3240,7 +3240,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 32,
|
||||
'task_id' => 85,
|
||||
'userid' => 1,
|
||||
'detail' => '子任务标记已完成:公司介绍',
|
||||
'detail' => '子标记任务已完成:公司介绍',
|
||||
'created_at' => seeders_at('2021-07-01 16:17:31'),
|
||||
'updated_at' => seeders_at('2021-07-01 16:17:31'),
|
||||
),
|
||||
@ -3251,7 +3251,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 32,
|
||||
'task_id' => 85,
|
||||
'userid' => 1,
|
||||
'detail' => '子任务标记已完成:新闻动态',
|
||||
'detail' => '子标记任务已完成:新闻动态',
|
||||
'created_at' => seeders_at('2021-07-01 16:17:32'),
|
||||
'updated_at' => seeders_at('2021-07-01 16:17:32'),
|
||||
),
|
||||
@ -3262,7 +3262,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 32,
|
||||
'task_id' => 85,
|
||||
'userid' => 1,
|
||||
'detail' => '子任务标记已完成:产品介绍',
|
||||
'detail' => '子标记任务已完成:产品介绍',
|
||||
'created_at' => seeders_at('2021-07-01 16:17:34'),
|
||||
'updated_at' => seeders_at('2021-07-01 16:17:34'),
|
||||
),
|
||||
@ -3273,7 +3273,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 32,
|
||||
'task_id' => 85,
|
||||
'userid' => 1,
|
||||
'detail' => '子任务标记已完成:案例展示',
|
||||
'detail' => '子标记任务已完成:案例展示',
|
||||
'created_at' => seeders_at('2021-07-01 16:17:35'),
|
||||
'updated_at' => seeders_at('2021-07-01 16:17:35'),
|
||||
),
|
||||
@ -3284,7 +3284,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 32,
|
||||
'task_id' => 85,
|
||||
'userid' => 1,
|
||||
'detail' => '子任务标记已完成:联系我们',
|
||||
'detail' => '子标记任务已完成:联系我们',
|
||||
'created_at' => seeders_at('2021-07-01 16:17:36'),
|
||||
'updated_at' => seeders_at('2021-07-01 16:17:36'),
|
||||
),
|
||||
@ -3295,7 +3295,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 32,
|
||||
'task_id' => 85,
|
||||
'userid' => 1,
|
||||
'detail' => '任务标记已完成:产品官网设计',
|
||||
'detail' => '标记任务已完成:产品官网设计',
|
||||
'created_at' => seeders_at('2021-07-01 16:17:37'),
|
||||
'updated_at' => seeders_at('2021-07-01 16:17:37'),
|
||||
),
|
||||
@ -3306,7 +3306,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 32,
|
||||
'task_id' => 85,
|
||||
'userid' => 1,
|
||||
'detail' => '子任务标记未完成:联系我们',
|
||||
'detail' => '标记子任务未完成:联系我们',
|
||||
'created_at' => seeders_at('2021-07-01 16:17:41'),
|
||||
'updated_at' => seeders_at('2021-07-01 16:17:41'),
|
||||
),
|
||||
@ -3317,7 +3317,7 @@ class ProjectLogsTableSeeder extends Seeder
|
||||
'column_id' => 32,
|
||||
'task_id' => 85,
|
||||
'userid' => 1,
|
||||
'detail' => '任务标记未完成:产品官网设计',
|
||||
'detail' => '标记任务未完成:产品官网设计',
|
||||
'created_at' => seeders_at('2021-07-01 16:17:44'),
|
||||
'updated_at' => seeders_at('2021-07-01 16:17:44'),
|
||||
),
|
||||
|
@ -28,6 +28,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 2,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 1,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '说明:将进度分成多级
|
||||
每张卡片为一个项目任务,标签表示任务状况
|
||||
通过将卡片拖至不同的进度列表下,来表示各项目进度',
|
||||
@ -54,6 +56,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 2,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 1,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '官网项目',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -78,7 +82,9 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 2,
|
||||
'dialog_id' => 0,
|
||||
'name' => '1',
|
||||
'flow_item_id' => 1,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '新增职位需求',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
@ -102,7 +108,9 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 2,
|
||||
'dialog_id' => 0,
|
||||
'name' => '11',
|
||||
'flow_item_id' => 1,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '更新公司简介',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
@ -126,6 +134,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 3,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 1,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '官网项目四期',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -150,6 +160,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 4,
|
||||
'dialog_id' => 16,
|
||||
'flow_item_id' => 1,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '官网项目三期',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -174,6 +186,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 5,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 1,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '官网项目二期',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -198,6 +212,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 6,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 3,
|
||||
'flow_item_name' => 'end|已完成',
|
||||
'name' => '官网项目一期',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -222,6 +238,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 7,
|
||||
'dialog_id' => 18,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '免费高品质的Sketch资源',
|
||||
'color' => '',
|
||||
'desc' => 'https://sketchrepo.com/',
|
||||
@ -246,6 +264,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 7,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '免费高品质的PSD资源',
|
||||
'color' => '',
|
||||
'desc' => 'https://psdrepo.com/',
|
||||
@ -270,6 +290,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 7,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '免费高清灵感图片网(偏文艺)',
|
||||
'color' => '',
|
||||
'desc' => 'https://magdeleine.co/',
|
||||
@ -294,6 +316,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 7,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '每天发现充满灵感且美丽的图片',
|
||||
'color' => '',
|
||||
'desc' => 'https://weheartit.com/',
|
||||
@ -318,6 +342,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 8,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '花瓣网:设计师寻找灵感的天堂',
|
||||
'color' => '',
|
||||
'desc' => 'https://huaban.com/',
|
||||
@ -342,6 +368,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 8,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'WallHaven:高清壁纸图片搜索引擎',
|
||||
'color' => '',
|
||||
'desc' => 'https://wallhaven.typepad.com/',
|
||||
@ -366,6 +394,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 8,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Pexels:免费高品质图片 可商用',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.pexels.com/',
|
||||
@ -390,6 +420,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 9,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Retinize:iOS切图神器',
|
||||
'color' => '',
|
||||
'desc' => 'http://retinize.it/',
|
||||
@ -414,6 +446,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 9,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'GuideGuide:一款PS参考线插件',
|
||||
'color' => '',
|
||||
'desc' => 'https://guideguide.me/photoshop/',
|
||||
@ -438,6 +472,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 9,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Assistor PS:一个功能强大的PS辅助工具',
|
||||
'color' => '',
|
||||
'desc' => 'http://wit-web.azurewebsites.net/assistor/download',
|
||||
@ -462,6 +498,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 10,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Fontello:图标字体生成器',
|
||||
'color' => '',
|
||||
'desc' => 'http://fontello.com/',
|
||||
@ -486,6 +524,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 10,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'inconfont: 免费提供高度可辨识符号图标',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.iconfont.cn/',
|
||||
@ -510,6 +550,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 10,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'The Noun Project:免费提供高度可辨识符号图标',
|
||||
'color' => '',
|
||||
'desc' => 'https://thenounproject.com/',
|
||||
@ -534,6 +576,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 10,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'EasyIcon:免费图标搜索和下载平台',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.easyicon.net/',
|
||||
@ -558,6 +602,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 10,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Icon Deposit:一个奇妙的图标下载站',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.icondeposit.com/',
|
||||
@ -582,6 +628,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 10,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'iConify:Mac平台的苹果应用图标自动化生成工具',
|
||||
'color' => '',
|
||||
'desc' => 'https://iconify.net/',
|
||||
@ -606,6 +654,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 11,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Logaster:教你在线几分钟内搞定专业的LOGO',
|
||||
'color' => '',
|
||||
'desc' => '设计https://www.logaster.cn/',
|
||||
@ -630,6 +680,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 11,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'LogoLounge:国际知名的LOGO设计权威网站',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.logolounge.com/',
|
||||
@ -654,6 +706,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 11,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'LogoMoose:一个优秀的logo素材站点',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.logomoose.com/',
|
||||
@ -678,6 +732,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 11,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'LOGOED:一个展示Logo设计的博客',
|
||||
'color' => '',
|
||||
'desc' => 'http://www.logoed.co.uk/page/2/',
|
||||
@ -702,6 +758,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 11,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Logo of the Day:汇集世界各地优秀LOGO作品的站点',
|
||||
'color' => '',
|
||||
'desc' => 'https://logooftheday.com/',
|
||||
@ -726,6 +784,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 5,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 1,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'asdasdad1111',
|
||||
'color' => '',
|
||||
'desc' => '7777777',
|
||||
@ -750,6 +810,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 5,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 1,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'hjhjhjjh',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -774,6 +836,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 11,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Logo of the Day:汇集世界各地优秀LOGO作品的站点',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -798,6 +862,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 11,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'LogoDesignLove:Logo设计技巧分享网',
|
||||
'color' => '',
|
||||
'desc' => 'LogoDesignLove:Logo设计技巧分享网',
|
||||
@ -822,6 +888,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 11,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'LogoDesignLove:Logo设计技巧分享网',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -846,6 +914,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 12,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Coolors:自动生成配色色板的小工具',
|
||||
'color' => '',
|
||||
'desc' => 'https://coolors.co/',
|
||||
@ -870,6 +940,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 12,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Material Palette:Material Design专用在线配色工具',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.materialpalette.com/',
|
||||
@ -894,6 +966,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 12,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Web安全色:WEB设计、开发中常用的安全颜色',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.bootcss.com/p/websafecolors/',
|
||||
@ -918,6 +992,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 12,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'ColorZilla:火狐浏览器网页取色器插件',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.colorzilla.com/',
|
||||
@ -942,6 +1018,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 12,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'Color Palette Generator:图片配色工具',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -966,6 +1044,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 12,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'inconfont: 免费提供高度可辨识符号图标',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.iconfont.cn/',
|
||||
@ -990,6 +1070,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 12,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'inconfont: 免费提供高度可辨识符号图标https://www.iconfont.cn/',
|
||||
'color' => '',
|
||||
'desc' => 'https://www.iconfont.cn/',
|
||||
@ -1014,6 +1096,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 3,
|
||||
'column_id' => 12,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 5,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => 'https://www.iconfont.cn/',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1038,6 +1122,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 13,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '将收集箱的事务进行判断。要立即执行:进入Q2列表。非立即执行:判断——1.不做(删掉)、2.稍晚再做(进入「稍后做」)、3.可做可不做的任务或可能有用的资源(进入「Mark」列表)',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1062,6 +1148,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 13,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '2分钟内能做完贴上2分钟标签(进入「2分钟速战」列表)。2分钟以上做完的事务进入Q3',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1086,6 +1174,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 13,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '判断能否一步做完,能进入Q4,不能打上多步标签(进入「项目清单」);或将多步骤任务分解成多个一步做完任务,进入Q4。',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1110,6 +1200,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 13,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '如果自己做,打上自己做标签(进入「执行清单」);如果别人做,打上别人做标签(进入「等待清单」)。',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1134,6 +1226,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 2,
|
||||
'column_id' => 2,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 1,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '❓❗ 说明:将进度分成多级
|
||||
|
||||
每张卡片为一个项目任务,标签表示任务状况
|
||||
@ -1162,6 +1256,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 13,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '每天晚上复查整套流程',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1186,6 +1282,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 13,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '1.检查「收集箱」是否清空。',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1210,6 +1308,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 13,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '2.检查「Mark」是否有条目需要执行,转化成行动或项目。',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1234,6 +1334,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 13,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '3.检查「项目清单」了解项目进度。',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1258,6 +1360,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 13,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '4.检查「等待清单」是否有条目需要转化成行动,也就是催促。',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1282,6 +1386,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 13,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '5.检查各清单是否有已完成,完成的、已作废的卡片,立刻删除。',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1306,6 +1412,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 14,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '任何事务或信息,先以最简单的方式记录到“收集箱”。然后判断,贴上标签后,拖动到相应列表里',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1330,6 +1438,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 15,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '优先级1:立即去做的事',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1354,6 +1464,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 14,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '任何事务或信息,先以最简单的方式记录到“收集箱”。然后判断,贴上标签后,拖动到相应列表里',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1378,6 +1490,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 16,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '优先级2:主要的执行清单',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1402,6 +1516,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 17,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '优先级3:存放需要多步骤做的事,持续追踪',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1426,6 +1542,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 17,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '可以为这个多步骤项目单独建一个项目,并把项目的链接放到卡片的详情页里,点击就能跳转进去。',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1450,6 +1568,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 17,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '活动策划',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1474,6 +1594,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 16,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '填问卷',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1498,6 +1620,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 18,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '存放等待协同的事',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1522,6 +1646,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 18,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '设计稿反馈',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1546,6 +1672,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 18,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '存放等待协同的事',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1570,6 +1698,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 19,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '存放稍后做的任务',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1594,6 +1724,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 19,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '阅实习生简历',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1618,6 +1750,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 20,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '存放可做可不做的任务,以及各种可能用到的资源',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1642,6 +1776,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 4,
|
||||
'column_id' => 20,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 9,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '团建KTV',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1666,6 +1802,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 5,
|
||||
'column_id' => 21,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 13,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '产品新需求',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1690,6 +1828,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 5,
|
||||
'column_id' => 22,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 13,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '需要调研的需求',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1714,6 +1854,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 5,
|
||||
'column_id' => 23,
|
||||
'dialog_id' => 17,
|
||||
'flow_item_id' => 13,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '进入交互设计的需求',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1738,6 +1880,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 5,
|
||||
'column_id' => 25,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 13,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '设计稿(放入设计稿)',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1762,6 +1906,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 5,
|
||||
'column_id' => 25,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 13,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '提测(放入提测时间规划表)',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1786,6 +1932,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 5,
|
||||
'column_id' => 25,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 13,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '发布流程(放入发布流程,应用文案等)',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1810,6 +1958,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 5,
|
||||
'column_id' => 25,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 13,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '-----上线需求-----(将左边列表中的需求拖动至下方,表示本版本上线需求)',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1834,6 +1984,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 6,
|
||||
'column_id' => 26,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 17,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '将小组一年工作目标拆解,设置出里程碑时间节点。并指派相关责任人。用标签来展示进行状况',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1858,6 +2010,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 6,
|
||||
'column_id' => 27,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 19,
|
||||
'flow_item_name' => 'end|已完成',
|
||||
'name' => '🚴 里程碑 1 需求评审完成,资源准备到位',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1882,6 +2036,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 6,
|
||||
'column_id' => 27,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 17,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '🚴 里程碑 2 设计完成,进行评审',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1906,6 +2062,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 6,
|
||||
'column_id' => 28,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 17,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '🚴 里程碑 3 产品开发完成,开始提测',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1930,6 +2088,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 6,
|
||||
'column_id' => 28,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 17,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '🚴 里程碑 4 测试完成准备线上发布',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -1954,11 +2114,13 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 6,
|
||||
'column_id' => 28,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 17,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '测试1',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
'end_at' => NULL,
|
||||
'start_at' => seeders_at('2021-07-01 16:33:00'),
|
||||
'end_at' => seeders_at('2021-07-02 23:59:00'),
|
||||
'archived_at' => NULL,
|
||||
'archived_userid' => 0,
|
||||
'complete_at' => NULL,
|
||||
@ -1978,11 +2140,13 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 6,
|
||||
'column_id' => 28,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 17,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '测试2',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
'end_at' => NULL,
|
||||
'start_at' => seeders_at('2021-07-01 16:33:00'),
|
||||
'end_at' => seeders_at('2021-07-02 23:59:00'),
|
||||
'archived_at' => NULL,
|
||||
'archived_userid' => 0,
|
||||
'complete_at' => NULL,
|
||||
@ -2002,11 +2166,13 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 6,
|
||||
'column_id' => 28,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 17,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '测试3',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
'end_at' => NULL,
|
||||
'start_at' => seeders_at('2021-07-01 16:33:00'),
|
||||
'end_at' => seeders_at('2021-07-02 23:59:00'),
|
||||
'archived_at' => NULL,
|
||||
'archived_userid' => 0,
|
||||
'complete_at' => NULL,
|
||||
@ -2026,6 +2192,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 6,
|
||||
'column_id' => 29,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 17,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '🚴 里程碑 5 市场发布',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -2050,6 +2218,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 7,
|
||||
'column_id' => 32,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 21,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '产品官网设计',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -2074,11 +2244,13 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 7,
|
||||
'column_id' => 32,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 23,
|
||||
'flow_item_name' => 'end|已完成',
|
||||
'name' => '首页',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
'end_at' => NULL,
|
||||
'start_at' => seeders_at('2021-07-01 16:15:55'),
|
||||
'end_at' => seeders_at('2021-07-02 16:15:55'),
|
||||
'archived_at' => NULL,
|
||||
'archived_userid' => 0,
|
||||
'complete_at' => seeders_at('2021-07-01 16:17:30'),
|
||||
@ -2098,11 +2270,13 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 7,
|
||||
'column_id' => 32,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 23,
|
||||
'flow_item_name' => 'end|已完成',
|
||||
'name' => '公司介绍',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
'end_at' => NULL,
|
||||
'start_at' => seeders_at('2021-07-01 16:15:55'),
|
||||
'end_at' => seeders_at('2021-07-02 16:15:55'),
|
||||
'archived_at' => NULL,
|
||||
'archived_userid' => 0,
|
||||
'complete_at' => seeders_at('2021-07-01 16:17:31'),
|
||||
@ -2122,11 +2296,13 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 7,
|
||||
'column_id' => 32,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 23,
|
||||
'flow_item_name' => 'end|已完成',
|
||||
'name' => '新闻动态',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
'end_at' => NULL,
|
||||
'start_at' => seeders_at('2021-07-01 16:15:55'),
|
||||
'end_at' => seeders_at('2021-07-02 16:15:55'),
|
||||
'archived_at' => NULL,
|
||||
'archived_userid' => 0,
|
||||
'complete_at' => seeders_at('2021-07-01 16:17:32'),
|
||||
@ -2146,11 +2322,13 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 7,
|
||||
'column_id' => 32,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 23,
|
||||
'flow_item_name' => 'end|已完成',
|
||||
'name' => '产品介绍',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
'end_at' => NULL,
|
||||
'start_at' => seeders_at('2021-07-01 16:15:55'),
|
||||
'end_at' => seeders_at('2021-07-02 16:15:55'),
|
||||
'archived_at' => NULL,
|
||||
'archived_userid' => 0,
|
||||
'complete_at' => seeders_at('2021-07-01 16:17:34'),
|
||||
@ -2170,11 +2348,13 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 7,
|
||||
'column_id' => 32,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 23,
|
||||
'flow_item_name' => 'end|已完成',
|
||||
'name' => '案例展示',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
'end_at' => NULL,
|
||||
'start_at' => seeders_at('2021-07-01 16:15:55'),
|
||||
'end_at' => seeders_at('2021-07-02 16:15:55'),
|
||||
'archived_at' => NULL,
|
||||
'archived_userid' => 0,
|
||||
'complete_at' => seeders_at('2021-07-01 16:17:35'),
|
||||
@ -2194,11 +2374,13 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 7,
|
||||
'column_id' => 32,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 21,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '联系我们',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
'start_at' => NULL,
|
||||
'end_at' => NULL,
|
||||
'start_at' => seeders_at('2021-07-01 16:15:55'),
|
||||
'end_at' => seeders_at('2021-07-02 16:15:55'),
|
||||
'archived_at' => NULL,
|
||||
'archived_userid' => 0,
|
||||
'complete_at' => NULL,
|
||||
@ -2218,6 +2400,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 7,
|
||||
'column_id' => 32,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 21,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '官网新增一级栏目,“招聘信息”',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -2242,6 +2426,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 7,
|
||||
'column_id' => 33,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 21,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '产品官网',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
@ -2266,6 +2452,8 @@ class ProjectTasksTableSeeder extends Seeder
|
||||
'project_id' => 5,
|
||||
'column_id' => 24,
|
||||
'dialog_id' => 0,
|
||||
'flow_item_id' => 13,
|
||||
'flow_item_name' => 'start|待处理',
|
||||
'name' => '版本的确定',
|
||||
'color' => '',
|
||||
'desc' => '',
|
||||
|
@ -16,26 +16,24 @@ class SettingsTableSeeder extends Seeder
|
||||
{
|
||||
|
||||
|
||||
if (\DB::table('settings')->count() > 0) {
|
||||
if (\DB::table('settings')->where('name', 'system')->count() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
\DB::table('settings')->insert(array (
|
||||
0 =>
|
||||
array (
|
||||
'id' => 1,
|
||||
'name' => 'system',
|
||||
'desc' => '',
|
||||
'setting' => '{"reg":"open","login_code":"auto"}',
|
||||
'setting' => '{"reg":"open","project_invite":"open","login_code":"auto"}',
|
||||
'created_at' => seeders_at('2021-07-01 11:05:06'),
|
||||
'updated_at' => seeders_at('2021-07-01 12:27:12'),
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'id' => 2,
|
||||
'name' => 'priority',
|
||||
'desc' => '',
|
||||
'setting' => '[{"name":"\\u91cd\\u8981\\u4e14\\u7d27\\u6025","color":"#ED4014","days":1,"priority":1},{"name":"\\u91cd\\u8981\\u4e0d\\u7d27\\u6025","color":"#F16B62","days":3,"priority":2},{"name":"\\u7d27\\u6025\\u4e0d\\u91cd\\u8981","color":"#19C919","days":5,"priority":3},{"name":"\\u4e0d\\u91cd\\u8981\\u4e0d\\u7d27\\u6025","color":"#2D8CF0","days":7,"priority":4}]',
|
||||
'setting' => '[{"name":"\\u91cd\\u8981\\u4e14\\u7d27\\u6025","color":"#ED4014","days":1,"priority":1},{"name":"\\u91cd\\u8981\\u4e0d\\u7d27\\u6025","color":"#F16B62","days":3,"priority":2},{"name":"\\u7d27\\u6025\\u4e0d\\u91cd\\u8981","color":"#19C919","days":5,"priority":3},{"name":"\\u4e0d\\u91cd\\u8981\\u4e0d\\u7d27\\u6025","color":"#2D8CF0","days":0,"priority":4}]',
|
||||
'created_at' => seeders_at('2021-07-01 08:04:30'),
|
||||
'updated_at' => seeders_at('2021-07-01 09:20:26'),
|
||||
),
|
||||
|
@ -40,6 +40,7 @@ class UsersTableSeeder extends Seeder
|
||||
'line_at' => seeders_at('2021-07-01 17:43:48'),
|
||||
'task_dialog_id' => 18,
|
||||
'created_ip' => '',
|
||||
'disable_at' => null,
|
||||
'created_at' => seeders_at('2021-07-01 11:01:14'),
|
||||
'updated_at' => seeders_at('2021-07-01 17:43:48'),
|
||||
),
|
||||
@ -62,6 +63,7 @@ class UsersTableSeeder extends Seeder
|
||||
'line_at' => seeders_at('2021-07-01 16:57:40'),
|
||||
'task_dialog_id' => 16,
|
||||
'created_ip' => '',
|
||||
'disable_at' => null,
|
||||
'created_at' => seeders_at('2021-07-01 11:01:14'),
|
||||
'updated_at' => seeders_at('2021-07-01 16:58:00'),
|
||||
),
|
||||
|
File diff suppressed because one or more lines are too long
@ -3,11 +3,10 @@ version: '3'
|
||||
services:
|
||||
php:
|
||||
container_name: "dootask-php-${APP_ID}"
|
||||
image: "kuaifan/phpswoole:8.0"
|
||||
image: "kuaifan/php:swoole-8.0"
|
||||
shm_size: "1024m"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./docker/crontab/crontab.conf:/etc/supervisor/conf.d/crontab.conf
|
||||
- ./docker/dns/dns.conf:/etc/supervisor/conf.d/dns.conf
|
||||
- ./docker/php/php.conf:/etc/supervisor/conf.d/php.conf
|
||||
- ./docker/php/php.ini:/usr/local/etc/php/php.ini
|
||||
- ./docker/log/supervisor:/var/log/supervisor
|
||||
@ -42,11 +41,12 @@ services:
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: "${APP_IPPR}.3"
|
||||
depends_on:
|
||||
- php
|
||||
links:
|
||||
- php
|
||||
- office
|
||||
- fileview
|
||||
- drawio-webapp
|
||||
- drawio-export
|
||||
restart: unless-stopped
|
||||
|
||||
redis:
|
||||
@ -65,10 +65,12 @@ services:
|
||||
ports: # mysql ports item
|
||||
- "33062:3306" # mysql ports value
|
||||
volumes:
|
||||
- ./docker/mysql/repassword.sh:/etc/mysql/repassword.sh
|
||||
- ./docker/mysql/conf.d:/etc/mysql/conf.d
|
||||
- ./docker/mysql/data:/var/lib/mysql
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
MYSQL_PREFIX: "${DB_PREFIX}"
|
||||
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
|
||||
MYSQL_DATABASE: "${DB_DATABASE}"
|
||||
MYSQL_USER: "${DB_USERNAME}"
|
||||
@ -80,13 +82,16 @@ services:
|
||||
|
||||
office:
|
||||
container_name: "dootask-office-${APP_ID}"
|
||||
image: "onlyoffice/documentserver:6.3.1.32"
|
||||
image: "onlyoffice/documentserver:7.0.0.132"
|
||||
volumes:
|
||||
- ./docker/office/data:/var/www/onlyoffice/Data
|
||||
- ./docker/office/logs:/var/log/onlyoffice
|
||||
- ./docker/office/resources/documenteditor/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/documenteditor/main/resources/css/app.css
|
||||
- ./docker/office/resources/presentationeditor/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/presentationeditor/main/resources/css/app.css
|
||||
- ./docker/office/resources/spreadsheeteditor/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/spreadsheeteditor/main/resources/css/app.css
|
||||
- ./docker/office/resources/documenteditor/mobile/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/documenteditor/mobile/css/app.css
|
||||
- ./docker/office/resources/presentationeditor/mobile/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/presentationeditor/mobile/css/app.css
|
||||
- ./docker/office/resources/spreadsheeteditor/mobile/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/spreadsheeteditor/mobile/css/app.css
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
networks:
|
||||
@ -94,6 +99,47 @@ services:
|
||||
ipv4_address: "${APP_IPPR}.6"
|
||||
restart: unless-stopped
|
||||
|
||||
fileview:
|
||||
container_name: "dootask-fileview-${APP_ID}"
|
||||
image: "kuaifan/fileview:4.1.0-SNAPSHOT-RC3"
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
KK_CONTEXT_PATH: "/fileview"
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: "${APP_IPPR}.7"
|
||||
restart: unless-stopped
|
||||
|
||||
drawio-webapp:
|
||||
container_name: "dootask-drawio-webapp-${APP_ID}"
|
||||
image: "jgraph/drawio:16.6.1"
|
||||
volumes:
|
||||
- ./docker/drawio/webapp/index.html:/usr/local/tomcat/webapps/draw/index.html
|
||||
- ./docker/drawio/webapp/stencils:/usr/local/tomcat/webapps/draw/stencils
|
||||
- ./docker/drawio/webapp/js/app.min.js:/usr/local/tomcat/webapps/draw/js/app.min.js
|
||||
- ./docker/drawio/webapp/js/croppie/croppie.min.css:/usr/local/tomcat/webapps/draw/js/croppie/croppie.min.css
|
||||
- ./docker/drawio/webapp/js/diagramly/ElectronApp.js:/usr/local/tomcat/webapps/draw/js/diagramly/ElectronApp.js
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: "${APP_IPPR}.8"
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
depends_on:
|
||||
- drawio-export
|
||||
restart: unless-stopped
|
||||
|
||||
drawio-export:
|
||||
container_name: "dootask-drawio-export-${APP_ID}"
|
||||
image: "jgraph/export-server"
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: "${APP_IPPR}.9"
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
volumes:
|
||||
- ./docker/drawio/export/fonts:/usr/share/fonts/drawio
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
extnetwork:
|
||||
name: "dootask-networks-${APP_ID}"
|
||||
|
1
docker/crontab/.gitignore
vendored
Normal file
1
docker/crontab/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.log
|
@ -1,3 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
curl "http://127.0.0.1:20000/crontab" >> /dev/null 2>&1
|
||||
echo "$(date "+%Y-%m-%d %H:%M:%S")" > /var/www/docker/crontab/last.log
|
||||
curl "http://127.0.0.1:20000/crontab" >> /var/www/docker/crontab/last.log
|
||||
echo "\n$(date "+%Y-%m-%d %H:%M:%S")" >> /var/www/docker/crontab/last.log
|
||||
|
@ -1,10 +0,0 @@
|
||||
[program:dns]
|
||||
directory=/var/www/docker/dns
|
||||
command=/etc/init.d/dnsmasq restart
|
||||
numprocs=1
|
||||
autostart=true
|
||||
autorestart=false
|
||||
startretries=1
|
||||
user=root
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/var/log/supervisor/%(program_name)s.log
|
1
docker/drawio/export/fonts/.gitignore
vendored
Normal file
1
docker/drawio/export/fonts/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
|
17
docker/drawio/webapp/README.md
Normal file
17
docker/drawio/webapp/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Change
|
||||
|
||||
## js/diagramly/ElectronApp.js
|
||||
|
||||
- 隐藏文件中的无用菜单
|
||||
|
||||
## js/app.min.js
|
||||
|
||||
- 隐藏帮助菜单
|
||||
- 取消未保存关闭窗口提示
|
||||
- `EmbedFile.prototype.getTitle=...` 改 `EmbedFile.prototype.getTitle=function(){return this.desc.title||(urlParams.title?decodeURIComponent(urlParams.title):"")}`
|
||||
- `c.insertTemplateEnabled&&!c.isOffline()&&this.addMenuItems(b,["insertTemplate"],d)` 改 `c.insertTemplateEnabled&&this.addMenuItems(b,["insertTemplate"],d)`
|
||||
- `390:270` 改 `390:285`
|
||||
|
||||
## index.html
|
||||
|
||||
- 隐藏加载中的提示
|
477
docker/drawio/webapp/index.html
Normal file
477
docker/drawio/webapp/index.html
Normal file
@ -0,0 +1,477 @@
|
||||
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=5" ><![endif]-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Flowchart Maker & Online Diagram Software</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="Description" content="diagrams.net is free online diagram software for making flowcharts, process diagrams, org charts, UML, ER and network diagrams">
|
||||
<meta name="Keywords" content="diagram, online, flow chart, flowchart maker, uml, erd">
|
||||
<meta itemprop="name" content="diagrams.net - free flowchart maker and diagrams online">
|
||||
<meta itemprop="description" content="diagrams.net is a free online diagramming application and flowchart maker . You can use it to create UML, entity relationship,
|
||||
org charts, BPMN and BPM, database schema and networks. Also possible are telecommunication network, workflow, flowcharts, maps overlays and GIS, electronic
|
||||
circuit and social network diagrams.">
|
||||
<meta itemprop="image" content="https://lh4.googleusercontent.com/-cLKEldMbT_E/Tx8qXDuw6eI/AAAAAAAAAAs/Ke0pnlk8Gpg/w500-h344-k/BPMN%2Bdiagram%2Brc2f.png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="msapplication-config" content="images/browserconfig.xml">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="theme-color" content="#d89000">
|
||||
<script type="text/javascript">
|
||||
window.EXPORT_URL = window.location.origin + "/drawio/export/";
|
||||
window.DRAWIO_LIGHTBOX_URL = window.location.origin + "/drawio/webapp";
|
||||
|
||||
/**
|
||||
* URL Parameters and protocol description are here:
|
||||
*
|
||||
* https://desk.draw.io/support/solutions/articles/16000042546-what-url-parameters-are-supported
|
||||
*
|
||||
* Parameters for developers:
|
||||
*
|
||||
* - dev=1: For developers only
|
||||
* - test=1: For developers only
|
||||
* - export=URL for export: For developers only
|
||||
* - ignoremime=1: For developers only (see DriveClient.js). Use Cmd-S to override mime.
|
||||
* - createindex=1: For developers only (see etc/build/README)
|
||||
* - filesupport=0: For developers only (see Editor.js in core)
|
||||
* - savesidebar=1: For developers only (see Sidebar.js)
|
||||
* - pages=1: For developers only (see Pages.js)
|
||||
* - lic=email: For developers only (see LicenseServlet.java)
|
||||
* --
|
||||
* - networkshapes=1: For testing network shapes (temporary)
|
||||
*/
|
||||
var urlParams = (function()
|
||||
{
|
||||
var result = new Object();
|
||||
var params = window.location.search.slice(1).split('&');
|
||||
|
||||
for (var i = 0; i < params.length; i++)
|
||||
{
|
||||
idx = params[i].indexOf('=');
|
||||
|
||||
if (idx > 0)
|
||||
{
|
||||
result[params[i].substring(0, idx)] = params[i].substring(idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
})();
|
||||
|
||||
// Forces CDN caches by passing URL parameters via URL hash
|
||||
if (window.location.hash != null && window.location.hash.substring(0, 2) == '#P')
|
||||
{
|
||||
try
|
||||
{
|
||||
urlParams = JSON.parse(decodeURIComponent(window.location.hash.substring(2)));
|
||||
|
||||
if (urlParams.hash != null)
|
||||
{
|
||||
window.location.hash = urlParams.hash;
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// Global variable for desktop
|
||||
var mxIsElectron = window && window.process && window.process.type;
|
||||
|
||||
// Redirects page if required
|
||||
if (urlParams['dev'] != '1')
|
||||
{
|
||||
(function()
|
||||
{
|
||||
var proto = window.location.protocol;
|
||||
|
||||
if (!mxIsElectron)
|
||||
{
|
||||
var host = window.location.host;
|
||||
|
||||
// Redirects apex, drive and rt to www
|
||||
if (host === 'draw.io' || host === 'rt.draw.io' || host === 'drive.draw.io')
|
||||
{
|
||||
host = 'www.draw.io';
|
||||
}
|
||||
|
||||
var href = proto + '//' + host + window.location.href.substring(
|
||||
window.location.protocol.length +
|
||||
window.location.host.length + 2);
|
||||
|
||||
// Redirects if href changes
|
||||
if (href != window.location.href)
|
||||
{
|
||||
window.location.href = href;
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds meta tag to the page.
|
||||
*/
|
||||
function mxmeta(name, content, httpEquiv)
|
||||
{
|
||||
try
|
||||
{
|
||||
var s = document.createElement('meta');
|
||||
|
||||
if (name != null)
|
||||
{
|
||||
s.setAttribute('name', name);
|
||||
}
|
||||
|
||||
s.setAttribute('content', content);
|
||||
|
||||
if (httpEquiv != null)
|
||||
{
|
||||
s.setAttribute('http-equiv', httpEquiv);
|
||||
}
|
||||
|
||||
var t = document.getElementsByTagName('meta')[0];
|
||||
t.parentNode.insertBefore(s, t);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Synchronously adds scripts to the page.
|
||||
*/
|
||||
function mxscript(src, onLoad, id, dataAppKey, noWrite)
|
||||
{
|
||||
var defer = onLoad == null && !noWrite;
|
||||
|
||||
if ((urlParams['dev'] != '1' && typeof document.createElement('canvas').getContext === "function") ||
|
||||
onLoad != null || noWrite)
|
||||
{
|
||||
var s = document.createElement('script');
|
||||
s.setAttribute('type', 'text/javascript');
|
||||
s.setAttribute('defer', 'true');
|
||||
s.setAttribute('src', src);
|
||||
|
||||
if (id != null)
|
||||
{
|
||||
s.setAttribute('id', id);
|
||||
}
|
||||
|
||||
if (dataAppKey != null)
|
||||
{
|
||||
s.setAttribute('data-app-key', dataAppKey);
|
||||
}
|
||||
|
||||
if (onLoad != null)
|
||||
{
|
||||
var r = false;
|
||||
|
||||
s.onload = s.onreadystatechange = function()
|
||||
{
|
||||
if (!r && (!this.readyState || this.readyState == 'complete'))
|
||||
{
|
||||
r = true;
|
||||
onLoad();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var t = document.getElementsByTagName('script')[0];
|
||||
|
||||
if (t != null)
|
||||
{
|
||||
t.parentNode.insertBefore(s, t);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
document.write('<script src="' + src + '"' + ((id != null) ? ' id="' + id +'" ' : '') +
|
||||
((dataAppKey != null) ? ' data-app-key="' + dataAppKey +'" ' : '') + '></scr' + 'ipt>');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously adds scripts to the page.
|
||||
*/
|
||||
function mxinclude(src)
|
||||
{
|
||||
var g = document.createElement('script');
|
||||
g.type = 'text/javascript';
|
||||
g.async = true;
|
||||
g.src = src;
|
||||
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(g, s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds meta tags with application name (depends on offline URL parameter)
|
||||
*/
|
||||
(function()
|
||||
{
|
||||
var name = 'diagrams.net';
|
||||
mxmeta('apple-mobile-web-app-title', name);
|
||||
mxmeta('application-name', name);
|
||||
|
||||
if (mxIsElectron)
|
||||
{
|
||||
mxmeta(null, 'default-src \'self\' \'unsafe-inline\'; connect-src \'self\' https://*.draw.io https://fonts.googleapis.com https://fonts.gstatic.com; img-src * data:; media-src *; font-src *; style-src-elem \'self\' \'unsafe-inline\' https://fonts.googleapis.com', 'Content-Security-Policy');
|
||||
}
|
||||
})();
|
||||
|
||||
// Checks for local storage
|
||||
var isLocalStorage = false;
|
||||
|
||||
try
|
||||
{
|
||||
isLocalStorage = urlParams['local'] != '1' && typeof(localStorage) != 'undefined';
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
var mxScriptsLoaded = false, mxWinLoaded = false;
|
||||
|
||||
function checkAllLoaded()
|
||||
{
|
||||
if (mxScriptsLoaded && mxWinLoaded)
|
||||
{
|
||||
App.main();
|
||||
}
|
||||
};
|
||||
|
||||
var t0 = new Date();
|
||||
|
||||
// Changes paths for local development environment
|
||||
if (urlParams['dev'] == '1')
|
||||
{
|
||||
// Used to request grapheditor/mxgraph sources in dev mode
|
||||
var mxDevUrl = document.location.protocol + '//devhost.jgraph.com/drawio/src/main';
|
||||
|
||||
// Used to request draw.io sources in dev mode
|
||||
var drawDevUrl = document.location.protocol + '//devhost.jgraph.com/drawio/src/main/webapp/';
|
||||
var geBasePath = drawDevUrl + '/js/grapheditor';
|
||||
var mxBasePath = mxDevUrl + '/mxgraph';
|
||||
|
||||
if (document.location.protocol == 'file:')
|
||||
{
|
||||
geBasePath = './js/grapheditor';
|
||||
mxBasePath = './mxgraph';
|
||||
drawDevUrl = './';
|
||||
|
||||
// Forces includes for dev environment in node.js
|
||||
mxForceIncludes = true;
|
||||
}
|
||||
|
||||
mxscript(drawDevUrl + 'js/PreConfig.js');
|
||||
mxscript(drawDevUrl + 'js/diagramly/Init.js');
|
||||
mxscript(geBasePath + '/Init.js');
|
||||
mxscript(mxBasePath + '/mxClient.js');
|
||||
|
||||
// Adds all JS code that depends on mxClient. This indirection via Devel.js is
|
||||
// required in some browsers to make sure mxClient.js (and the files that it
|
||||
// loads asynchronously) are available when the code loaded in Devel.js runs.
|
||||
mxscript(drawDevUrl + 'js/diagramly/Devel.js');
|
||||
|
||||
// Electron
|
||||
if (mxIsElectron)
|
||||
{
|
||||
mxscript('js/diagramly/DesktopLibrary.js');
|
||||
mxscript('js/diagramly/ElectronApp.js');
|
||||
}
|
||||
|
||||
mxscript(drawDevUrl + 'js/PostConfig.js');
|
||||
}
|
||||
else
|
||||
{
|
||||
(function()
|
||||
{
|
||||
var hostName = window.location.hostname;
|
||||
|
||||
// Supported domains are *.draw.io and the packaged version in Quip
|
||||
var supportedDomain = (hostName.substring(hostName.length - 8, hostName.length) === '.draw.io') ||
|
||||
(hostName.substring(hostName.length - 13, hostName.length) === '.diagrams.net');
|
||||
|
||||
function loadAppJS()
|
||||
{
|
||||
mxscript('js/app.min.js', function()
|
||||
{
|
||||
mxScriptsLoaded = true;
|
||||
checkAllLoaded();
|
||||
|
||||
// Electron
|
||||
if (mxIsElectron)
|
||||
{
|
||||
mxscript('js/diagramly/DesktopLibrary.js', function()
|
||||
{
|
||||
mxscript('js/diagramly/ElectronApp.js', function()
|
||||
{
|
||||
mxscript('js/extensions.min.js', function()
|
||||
{
|
||||
mxscript('js/stencils.min.js', function()
|
||||
{
|
||||
mxscript('js/shapes-14-6-5.min.js', function()
|
||||
{
|
||||
mxscript('js/PostConfig.js');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (!supportedDomain)
|
||||
{
|
||||
mxscript('js/PostConfig.js');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (!supportedDomain || mxIsElectron)
|
||||
{
|
||||
mxscript('js/PreConfig.js', loadAppJS);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadAppJS();
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
// Adds basic error handling
|
||||
window.onerror = function()
|
||||
{
|
||||
var status = document.getElementById('geStatus');
|
||||
|
||||
if (status != null)
|
||||
{
|
||||
status.innerHTML = 'Page could not be loaded. Please try refreshing.';
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/plgmlhohecdddhbmmkncjdmlhcmaachm">
|
||||
<link rel="stylesheet" type="text/css" href="js/croppie/croppie.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="styles/grapheditor.css">
|
||||
<link rel="preconnect" href="https://storage.googleapis.com">
|
||||
<link rel="canonical" href="https://app.diagrams.net">
|
||||
<link rel="manifest" href="images/manifest.json">
|
||||
<style type="text/css">
|
||||
body { overflow:hidden; }
|
||||
div.picker { z-index: 10007; }
|
||||
.geSidebarContainer .geTitle input {
|
||||
font-size:8pt;
|
||||
color:#606060;
|
||||
}
|
||||
.geBlock {
|
||||
display: none;
|
||||
z-index:-3;
|
||||
margin:100px;
|
||||
margin-top:40px;
|
||||
margin-bottom:30px;
|
||||
padding:20px;
|
||||
text-align:center;
|
||||
min-width:50%;
|
||||
}
|
||||
.geBlock h1, .geBlock h2 {
|
||||
margin-top:0px;
|
||||
padding-top:0px;
|
||||
}
|
||||
.geEditor *:not(.geScrollable)::-webkit-scrollbar {
|
||||
width:14px;
|
||||
height:14px;
|
||||
}
|
||||
.geEditor ::-webkit-scrollbar-track {
|
||||
background-clip:padding-box;
|
||||
border:solid transparent;
|
||||
border-width:1px;
|
||||
}
|
||||
.geEditor ::-webkit-scrollbar-corner {
|
||||
background-color:transparent;
|
||||
}
|
||||
.geEditor ::-webkit-scrollbar-thumb {
|
||||
background-color:rgba(0,0,0,.1);
|
||||
background-clip:padding-box;
|
||||
border:solid transparent;
|
||||
border-radius:10px;
|
||||
}
|
||||
.geEditor ::-webkit-scrollbar-thumb:hover {
|
||||
background-color:rgba(0,0,0,.4);
|
||||
}
|
||||
.geTemplate {
|
||||
border:1px solid transparent;
|
||||
display:inline-block;
|
||||
_display:inline;
|
||||
vertical-align:top;
|
||||
border-radius:3px;
|
||||
overflow:hidden;
|
||||
font-size:14pt;
|
||||
cursor:pointer;
|
||||
margin:5px;
|
||||
}
|
||||
.geDialog h2 {
|
||||
line-height: 1.2;
|
||||
}
|
||||
</style>
|
||||
<!-- Workaround for binary XHR in IE 9/10, see App.loadUrl -->
|
||||
<!--[if (IE 9)|(IE 10)]><!-->
|
||||
<script type="text/vbscript">
|
||||
Function mxUtilsBinaryToArray(Binary)
|
||||
Dim i
|
||||
ReDim byteArray(LenB(Binary))
|
||||
For i = 1 To LenB(Binary)
|
||||
byteArray(i-1) = AscB(MidB(Binary, i, 1))
|
||||
Next
|
||||
mxUtilsBinaryToArray = byteArray
|
||||
End Function
|
||||
</script>
|
||||
<!--<![endif]-->
|
||||
</head>
|
||||
<body class="geEditor">
|
||||
<div id="geInfo">
|
||||
<div class="geBlock">
|
||||
<h1>Flowchart Maker and Online Diagram Software</h1>
|
||||
<p>
|
||||
diagrams.net (formerly draw.io) is free online diagram software. You can use it as a flowchart maker, network diagram software, to create UML online, as an ER diagram tool,
|
||||
to design database schema, to build BPMN online, as a circuit diagram maker, and more. draw.io can import .vsdx, Gliffy™ and Lucidchart™ files .
|
||||
</p>
|
||||
<h2 id="geStatus">Loading...</h2>
|
||||
<p>
|
||||
Please ensure JavaScript is enabled.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
/**
|
||||
* Main
|
||||
*/
|
||||
if (navigator.userAgent != null && navigator.userAgent.toLowerCase().
|
||||
indexOf(' electron/') >= 0 && typeof process !== 'undefined' && process.versions.electron < 5)
|
||||
{
|
||||
// Redirects old Electron app to latest version
|
||||
var div = document.getElementById('geInfo');
|
||||
|
||||
if (div != null)
|
||||
{
|
||||
div.innerHTML = '<center><h2>You are using an out of date version of this app.<br>Please download the latest version ' +
|
||||
'<a href="https://github.com/jgraph/drawio-desktop/releases/latest" target="_blank">here</a>.</h2></center>';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (urlParams['dev'] != '1' && typeof document.createElement('canvas').getContext === "function")
|
||||
{
|
||||
window.addEventListener('load', function()
|
||||
{
|
||||
mxWinLoaded = true;
|
||||
checkAllLoaded();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
App.main();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
12282
docker/drawio/webapp/js/app.min.js
vendored
Normal file
12282
docker/drawio/webapp/js/app.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user