mirror of
https://github.com/flipped-aurora/gin-vue-admin.git
synced 2026-09-25 05:50:19 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fe1dbdc03 | ||
|
|
1c4ef7325e | ||
|
|
b2b68e5237 | ||
|
|
c59a0504ad | ||
|
|
182af9a1b6 | ||
|
|
424c5b23b4 | ||
|
|
86b420ce67 | ||
|
|
ea5709e5b6 | ||
|
|
4e9e3570ed | ||
|
|
78cdc287c5 | ||
|
|
58b123d316 | ||
|
|
fb199aeedc | ||
|
|
364dddb70a | ||
|
|
6bf848d53f | ||
|
|
3e429f4091 | ||
|
|
daabca4768 | ||
|
|
9e688c65d3 | ||
|
|
2e8f5870d8 | ||
|
|
8b0a00b98a | ||
|
|
1ce3e11d52 | ||
|
|
11aa8c5cc2 | ||
|
|
d8e6f2f5f4 | ||
|
|
9d5c93b4c3 | ||
|
|
2f7f3e5ac6 | ||
|
|
cb97d3832d | ||
|
|
dd34d6e3be | ||
|
|
1d700ad5b6 | ||
|
|
3648df9522 | ||
|
|
eb2925829f | ||
|
|
1e3c968277 | ||
|
|
339db778c3 | ||
|
|
8b8ae7b692 | ||
|
|
52be26eb43 | ||
|
|
9f958addc8 | ||
|
|
7a69b88885 | ||
|
|
76fe71a8dd | ||
|
|
3f21d7f8dd | ||
|
|
1fce169616 | ||
|
|
0d900293eb | ||
|
|
b71ad7b6f4 | ||
|
|
2139e38147 | ||
|
|
a033ab9abe | ||
|
|
311e1ddb9f | ||
|
|
31e6316916 | ||
|
|
9260111312 | ||
|
|
877f8f4fcf | ||
|
|
273d7bd50e | ||
|
|
39abc7b632 | ||
|
|
e19ddf6600 | ||
|
|
9db3bcfac5 | ||
|
|
a6b6b5d4aa | ||
|
|
23f9227da1 | ||
|
|
857ba194e5 | ||
|
|
6e27e3a114 | ||
|
|
d5b4bbfa84 | ||
|
|
72ee8fb6fc | ||
|
|
2ec2e9a66e | ||
|
|
bf095c38e9 | ||
|
|
cee3b54b4c | ||
|
|
ff7e002049 | ||
|
|
a1c8f6bc55 | ||
|
|
ca24945af6 | ||
|
|
ea7455e092 | ||
|
|
a5e1170af0 | ||
|
|
44751357b4 | ||
|
|
e58d40ab83 | ||
|
|
f7c3d76d37 | ||
|
|
55bf69c810 | ||
|
|
b4232a8f27 | ||
|
|
8d6dcc6b07 | ||
|
|
e3a57bdc69 | ||
|
|
0eee6a3cc2 | ||
|
|
174721bc55 | ||
|
|
ba63bf2371 | ||
|
|
2f07e9271f | ||
|
|
3e9602ff7e | ||
|
|
30763b48de | ||
|
|
c3b75933b8 | ||
|
|
ff08d45f25 | ||
|
|
be0e4e97f7 | ||
|
|
b916c7951b | ||
|
|
0ebb3d4195 | ||
|
|
e7680c43ac | ||
|
|
2f80eaaf9c | ||
|
|
2dfe9d7f4b | ||
|
|
cbc504cc0b | ||
|
|
99550cbc37 | ||
|
|
4a53864738 | ||
|
|
b731b9df78 | ||
|
|
1f3a8ea2eb | ||
|
|
2bd82ead97 | ||
|
|
9f1f72fce5 | ||
|
|
38ed8bb399 | ||
|
|
2d98fdf321 | ||
|
|
214e108591 | ||
|
|
b197a84ef7 | ||
|
|
09603c436e | ||
|
|
d4c883535b | ||
|
|
6323688fca | ||
|
|
7a7c811311 | ||
|
|
81e178b267 | ||
|
|
70c9a715d4 | ||
|
|
28bd6fef7e | ||
|
|
35e3f820c0 | ||
|
|
e847aa9e75 | ||
|
|
fcfa55b39a | ||
|
|
8be390b2cf | ||
|
|
3ad83cbcd6 | ||
|
|
41826f5e5c | ||
|
|
0ccd6dc750 | ||
|
|
8c2ce8587d | ||
|
|
5e35fcea58 | ||
|
|
54595d46e5 | ||
|
|
09cb29da17 | ||
|
|
ecf1b5bc56 | ||
|
|
da71231be9 | ||
|
|
63feecf4aa | ||
|
|
a6255557ee | ||
|
|
84a3c3ba7c | ||
|
|
90c8a240a1 | ||
|
|
07c694e24d | ||
|
|
7c1c392710 | ||
|
|
379f5fe841 | ||
|
|
0ab15cc5e2 | ||
|
|
8f7a8a9e6d | ||
|
|
d5dc42c71e | ||
|
|
6ae5b2384e | ||
|
|
07c9e97bf5 | ||
|
|
5a53da62e3 | ||
|
|
11748da7a9 | ||
|
|
b0e15b86f5 | ||
|
|
1d1ad23bc0 | ||
|
|
ac54446769 | ||
|
|
5fe5caaf2b | ||
|
|
e111b01f16 | ||
|
|
fd2ac3f7f1 | ||
|
|
4e57b75640 | ||
|
|
e439bff2bc | ||
|
|
e210002bff | ||
|
|
4e735beb2f | ||
|
|
d747edc1f3 | ||
|
|
8bec94336b | ||
|
|
83eb193f1b | ||
|
|
9806f72ca6 | ||
|
|
7e7ec52290 | ||
|
|
98746fbca5 | ||
|
|
c0f65386bd | ||
|
|
cef6fce2c9 | ||
|
|
4964518486 | ||
|
|
06ec433b4e | ||
|
|
85681af8b8 | ||
|
|
4b12ecdf4b | ||
|
|
e8eacb02d6 | ||
|
|
e9dd55f4f9 | ||
|
|
a79df727c9 | ||
|
|
ca47b1c4f5 | ||
|
|
ab75bc7f59 | ||
|
|
f776a061ce | ||
|
|
1456f95b2c | ||
|
|
c5767f6ad5 | ||
|
|
3f8dedba80 | ||
|
|
121d62f20f | ||
|
|
2c08265210 | ||
|
|
82e397f136 | ||
|
|
730ee0205e | ||
|
|
a4716d5db0 | ||
|
|
06f166b70a | ||
|
|
f2ea36ac0d | ||
|
|
a8e706a395 | ||
|
|
f608bfa191 | ||
|
|
e494e22c07 | ||
|
|
d40c815760 | ||
|
|
ea39d83907 | ||
|
|
dae38d9b18 | ||
|
|
1e7cf9e65d | ||
|
|
831bd61e02 | ||
|
|
fe2b846dbc | ||
|
|
8c47d2b179 | ||
|
|
afe6b234cf | ||
|
|
1743c763d2 | ||
|
|
21237f8be1 | ||
|
|
d19e446416 | ||
|
|
8b93ede959 | ||
|
|
cb23254152 | ||
|
|
601aa6e245 | ||
|
|
532d282509 | ||
|
|
93c0525ea5 | ||
|
|
2bf423b42f | ||
|
|
f6c2ce45c1 | ||
|
|
d24eeefc86 | ||
|
|
876d4187d9 | ||
|
|
2412e7151f | ||
|
|
07c21caf8d | ||
|
|
69ddb2ddff | ||
|
|
283143e1bf | ||
|
|
80c54ad890 | ||
|
|
3272f3c8cc | ||
|
|
6bfbfc4762 | ||
|
|
8d2735f6e9 | ||
|
|
d72984be7e | ||
|
|
39212666c5 | ||
|
|
c909d32c56 | ||
|
|
8073e989f5 | ||
|
|
4671013ea6 | ||
|
|
b954457ae4 | ||
|
|
f7889b9709 | ||
|
|
a9894acd51 | ||
|
|
51e1c62ee6 | ||
|
|
fbd0f4b9c6 | ||
|
|
58204373aa | ||
|
|
710257da65 | ||
|
|
f63803529a | ||
|
|
7d9af64f6d | ||
|
|
87a4ce17e1 | ||
|
|
fae6f413a5 | ||
|
|
d8e36ab0f8 | ||
|
|
35fd5c8d65 | ||
|
|
5604424e6f | ||
|
|
c4c5f01ee5 | ||
|
|
e6d203d567 | ||
|
|
e75fa0bd84 | ||
|
|
b2cfbb95b6 | ||
|
|
0d84ea9523 | ||
|
|
e99fe70164 | ||
|
|
8bd337e08f | ||
|
|
12124eb1aa | ||
|
|
e036395d18 | ||
|
|
41ba7ad6a5 | ||
|
|
3452eafe4f | ||
|
|
e515ad3074 | ||
|
|
255f138bdb | ||
|
|
08496c2c61 | ||
|
|
25a996fe51 | ||
|
|
9ba39f2eda | ||
|
|
4a70b38a99 | ||
|
|
0dc66adc0d | ||
|
|
c2c11b29a7 | ||
|
|
071f48c3ca | ||
|
|
65cef0b40f | ||
|
|
f30912e02f | ||
|
|
1b74dc8afd | ||
|
|
1840b343b2 | ||
|
|
4f8030ecae | ||
|
|
bb6c0be9de | ||
|
|
a2d4c43047 | ||
|
|
60184f12bd | ||
|
|
4ffa30e31a | ||
|
|
7fff2833c3 | ||
|
|
d1da92d040 | ||
|
|
6bb8f8a030 | ||
|
|
dcd0eb20e1 | ||
|
|
7d6270079a | ||
|
|
05242c0ad7 | ||
|
|
30fb3a97d7 | ||
|
|
4dec8cfb17 | ||
|
|
bb8210b88e | ||
|
|
b69858b6f7 | ||
|
|
f191601ab4 | ||
|
|
7d62a55a92 | ||
|
|
eadcda4e30 | ||
|
|
45382d1446 | ||
|
|
0fd4f0bd7c | ||
|
|
c3fd7b9077 | ||
|
|
0f6aa3df83 | ||
|
|
48333c2ad2 | ||
|
|
9786886c3f | ||
|
|
b0eedfd6f1 | ||
|
|
1753bf96a8 | ||
|
|
67315bf88e | ||
|
|
4a3dcf24e1 | ||
|
|
c1b18fe478 | ||
|
|
e918af0a0c | ||
|
|
a847ad292f | ||
|
|
efac72ecbb | ||
|
|
ed5611810f | ||
|
|
b8d5e79c03 | ||
|
|
3e72ea0650 | ||
|
|
8b8a0b61af | ||
|
|
f237ac981e | ||
|
|
0ce5a63c1d | ||
|
|
a483ac9174 | ||
|
|
91e4c89962 | ||
|
|
c34e3b4282 | ||
|
|
6e38eb1d70 | ||
|
|
5d227d8d72 | ||
|
|
ccdf725957 | ||
|
|
86cffba4aa | ||
|
|
2086a73557 | ||
|
|
a5e8980b23 | ||
|
|
360af93d8d | ||
|
|
4c46bdaa7f | ||
|
|
b98094ceee | ||
|
|
03330537fc | ||
|
|
2c7809b134 | ||
|
|
af287dcdce | ||
|
|
ed840a96e6 | ||
|
|
04cfd0eb38 | ||
|
|
6386034c65 | ||
|
|
9b26d6c504 | ||
|
|
3529297fa4 | ||
|
|
ad699fa23c | ||
|
|
0fd9e951b9 | ||
|
|
66bcd32ae7 | ||
|
|
f0d91b0058 | ||
|
|
3d58213f0b | ||
|
|
528d7411e1 | ||
|
|
a6651ffe48 | ||
|
|
a8e6dc3a3a | ||
|
|
8733b2463c | ||
|
|
37b0ef4599 | ||
|
|
c72ed970fb | ||
|
|
e9e42be139 | ||
|
|
b55ecd2808 | ||
|
|
f2a8829d1d | ||
|
|
c20ea7e030 | ||
|
|
df3fc0a9ef | ||
|
|
3c7a05b572 | ||
|
|
a155bcd11a | ||
|
|
57f13f59b1 | ||
|
|
894cf8e6c3 | ||
|
|
fa3b952af3 | ||
|
|
b26db1910b | ||
|
|
aab865be96 | ||
|
|
866fa5643e | ||
|
|
87ced16d63 | ||
|
|
ed5463360e | ||
|
|
27edfac161 | ||
|
|
7f70e69c8e | ||
|
|
c70fedfb43 | ||
|
|
85d7b5fd5d | ||
|
|
cbace6f97e | ||
|
|
bec5dc4476 | ||
|
|
f7ddd8bbbb | ||
|
|
43d0c291ed | ||
|
|
0956edd0d0 | ||
|
|
c9477d37fa | ||
|
|
3b5c96d7cb | ||
|
|
c3c049b225 | ||
|
|
6fedb9773b | ||
|
|
332a8de2e9 | ||
|
|
7121968081 | ||
|
|
a44133d0c9 | ||
|
|
3c3d9487d0 | ||
|
|
15b5083d17 | ||
|
|
e25cafe2b4 | ||
|
|
b5c44e47ff | ||
|
|
5d04b8deea | ||
|
|
1207291249 | ||
|
|
06bf641e7a | ||
|
|
ce764082b8 | ||
|
|
aebc301b05 | ||
|
|
3b93a073c3 | ||
|
|
a4a7b44da9 | ||
|
|
e65be6ee36 | ||
|
|
2d0b9ecac0 | ||
|
|
5a5b86aeec | ||
|
|
ff41fe0577 | ||
|
|
c97b60b2da | ||
|
|
2645dcdddf | ||
|
|
6debb09b0f | ||
|
|
62b84d454a | ||
|
|
26dc3f32d9 | ||
|
|
84f8b6e1da | ||
|
|
70e3d40c3e | ||
|
|
432b2f04d7 | ||
|
|
63c82cf705 | ||
|
|
5c7d121e67 | ||
|
|
055198ccd5 | ||
|
|
0993e81e00 | ||
|
|
15d22b7726 | ||
|
|
bb3a54a312 | ||
|
|
7ad6b9c32a | ||
|
|
4814f21017 | ||
|
|
1f962f21b3 | ||
|
|
397e1e2bf1 | ||
|
|
b10c7297fd | ||
|
|
7f9194f475 | ||
|
|
0fc1edf895 | ||
|
|
c973c0cce5 | ||
|
|
48f3bdc80c | ||
|
|
0fc43fcf42 | ||
|
|
e593ec604e | ||
|
|
1494bc9db3 | ||
|
|
4512aa7ff8 | ||
|
|
de782e629f | ||
|
|
b7c6a7ffd9 | ||
|
|
ca4118cc8e | ||
|
|
a561e7dc2b | ||
|
|
b3a0f187d7 | ||
|
|
39be2d0351 | ||
|
|
5492e0db50 | ||
|
|
db038c29e8 | ||
|
|
4fe1f5c225 | ||
|
|
ac374bab22 | ||
|
|
01f6814a25 | ||
|
|
6e4dc10c49 | ||
|
|
6b3a9024d3 | ||
|
|
b4841843f7 | ||
|
|
35efc07b2e | ||
|
|
954160ebd9 | ||
|
|
ea1633ad52 | ||
|
|
fb5d5ea1be | ||
|
|
02f5d6fea2 | ||
|
|
227755b892 | ||
|
|
eebd8771bd | ||
|
|
36fed0cc6f | ||
|
|
08f3b64243 | ||
|
|
59cfe96fe3 | ||
|
|
128e810f77 | ||
|
|
bbe9322f75 | ||
|
|
35186b41ff | ||
|
|
0c846f0fea | ||
|
|
d497e96407 | ||
|
|
a4f139f855 | ||
|
|
e386b28ba4 | ||
|
|
aa8ef48246 | ||
|
|
6bac462359 | ||
|
|
24e4d05157 | ||
|
|
737c051466 | ||
|
|
4ab50882a4 | ||
|
|
06a07865f3 | ||
|
|
5607702be9 | ||
|
|
e9be009f49 | ||
|
|
bb4e938bd2 | ||
|
|
45f723c07f | ||
|
|
640f44fd10 | ||
|
|
821dd02aa4 | ||
|
|
6e2140258e | ||
|
|
b628bd7769 | ||
|
|
f23cd22110 | ||
|
|
8ef4f408bf | ||
|
|
01139a2e6b | ||
|
|
08606f740d | ||
|
|
42c6bc11cc | ||
|
|
d6703e82e2 | ||
|
|
8f1692e394 | ||
|
|
3e55234a24 | ||
|
|
7df246c25a | ||
|
|
fbffbce090 | ||
|
|
18e9b6d84c | ||
|
|
03e5bc515f | ||
|
|
b336529d42 | ||
|
|
bbf716b28e | ||
|
|
725f6fe3ec | ||
|
|
f773a02ce0 | ||
|
|
21266be1ed | ||
|
|
53d0338218 | ||
|
|
2f67c23c5c | ||
|
|
e64be755f9 | ||
|
|
23952c5f5f | ||
|
|
02fc5036d7 | ||
|
|
a6a65ed9f4 | ||
|
|
369f657aa6 | ||
|
|
1801e5b30f | ||
|
|
b10d5103f3 | ||
|
|
9964bfef8b | ||
|
|
9945a19f2c | ||
|
|
a5d43e6f14 | ||
|
|
b61b9a06dd | ||
|
|
f28570fa98 | ||
|
|
fb53d639be | ||
|
|
42de9257c3 | ||
|
|
0fba597a61 | ||
|
|
d0393c4e9f | ||
|
|
a21d2a591e | ||
|
|
de54688c56 | ||
|
|
3c486d9117 | ||
|
|
e8128250f4 | ||
|
|
58a1b72910 | ||
|
|
ee00d29afd | ||
|
|
5cfcc538a8 | ||
|
|
e7c8a9d420 | ||
|
|
6b07ffe8f7 | ||
|
|
0d17340445 | ||
|
|
2a0b92713b | ||
|
|
fb22eb4178 | ||
|
|
136fb2f394 | ||
|
|
c515805f0a | ||
|
|
613ba9ba20 | ||
|
|
f6314d3581 | ||
|
|
043f5c78c2 | ||
|
|
0f9e519b0e | ||
|
|
0d17a2351f | ||
|
|
1b388664cb | ||
|
|
1edb0c529f | ||
|
|
f07a58bff4 | ||
|
|
c054840aec | ||
|
|
e259418398 | ||
|
|
df1d68a724 | ||
|
|
9d58390e11 | ||
|
|
db4c7a8bda | ||
|
|
ab163fc0ef | ||
|
|
0265801830 | ||
|
|
5fa7bbbc0f | ||
|
|
e15351e1c5 | ||
|
|
5eb0133b5c | ||
|
|
ff8344d671 | ||
|
|
d9a43ea1d6 | ||
|
|
6c62d73afb | ||
|
|
d23d6c342a | ||
|
|
90549a09e1 | ||
|
|
17d3639443 | ||
|
|
e8a096b8fe | ||
|
|
fea136e513 | ||
|
|
f3a4095462 | ||
|
|
d7d25cf69b | ||
|
|
3b33fe83e6 | ||
|
|
56f4d223ab | ||
|
|
c07317a619 | ||
|
|
4798f7688b | ||
|
|
24db0eb5aa | ||
|
|
1ece13c43d | ||
|
|
48268309b9 | ||
|
|
4074a52b1b | ||
|
|
61dd524c26 | ||
|
|
61a678a318 | ||
|
|
8ecec6a14f | ||
|
|
e3f8b8a394 | ||
|
|
eebddbafcc | ||
|
|
50cbce0f7a | ||
|
|
f9974a8971 | ||
|
|
baa48a1679 | ||
|
|
032910f87a | ||
|
|
773ef95b70 | ||
|
|
18c2f84771 | ||
|
|
ea721a71b5 | ||
|
|
b1b7427c6e | ||
|
|
7bc7c30510 | ||
|
|
d1e055430d | ||
|
|
736534379a | ||
|
|
f6b71e157c | ||
|
|
53e9f931f2 | ||
|
|
c226a9476f | ||
|
|
8ca5600286 | ||
|
|
9729bc00a0 | ||
|
|
f9c9af8261 | ||
|
|
c9e64373fb | ||
|
|
262febf486 | ||
|
|
eca9c4d7f8 | ||
|
|
a20083bb9c | ||
|
|
d31b01415b | ||
|
|
26cdb472f0 | ||
|
|
3c2595aaf9 | ||
|
|
ad323a0dff | ||
|
|
9a0417b1a7 | ||
|
|
3324a816ee | ||
|
|
cc4135a2d7 | ||
|
|
2d884c83cc | ||
|
|
619c5b5e1d | ||
|
|
33afa9adfc | ||
|
|
0c141cc6f7 | ||
|
|
efdff2ab0c | ||
|
|
ebefb0eba4 | ||
|
|
d4b9c2c97b | ||
|
|
d4db16b501 | ||
|
|
a5cbd7387b | ||
|
|
99d6b4a361 | ||
|
|
18365689e6 | ||
|
|
8ea827412a | ||
|
|
4d8979c4ad | ||
|
|
236357baa8 | ||
|
|
5ef4b21858 | ||
|
|
3b3441851c | ||
|
|
5517987e14 | ||
|
|
7e98f61bec | ||
|
|
e2d360e8b1 | ||
|
|
57cfedfd7c | ||
|
|
50b1d21d13 | ||
|
|
7b79ec6c66 | ||
|
|
7a5b31530f | ||
|
|
e9100a9542 | ||
|
|
421304d4a3 | ||
|
|
10d61bbbb3 | ||
|
|
095709fb19 | ||
|
|
725f4150fc | ||
|
|
93ac2e70d2 | ||
|
|
f4c2b50ca5 | ||
|
|
3354edbb1c | ||
|
|
d3600499af | ||
|
|
35e2c2cfe8 | ||
|
|
1e4970469f | ||
|
|
4cbfd7c9b1 | ||
|
|
deb32fa0fa | ||
|
|
f17a738780 | ||
|
|
2f53d0efae | ||
|
|
3e43c2e6e6 | ||
|
|
96919a9f79 | ||
|
|
a23fb627cd | ||
|
|
aae915b092 | ||
|
|
160c8e814b | ||
|
|
88f48baba1 | ||
|
|
7835efbf37 | ||
|
|
455fbb505e | ||
|
|
df3f7090e8 | ||
|
|
632b3c200c | ||
|
|
5b203326ae | ||
|
|
fd87aca57d | ||
|
|
b9b7e556e3 | ||
|
|
5b9a7e6a2a | ||
|
|
d278e19367 | ||
|
|
342030ca47 | ||
|
|
f90e241cc9 | ||
|
|
ff6bcc437c | ||
|
|
e32dbb5125 | ||
|
|
06701cc7d1 | ||
|
|
ccb31bc1b4 | ||
|
|
7e2e29e2e8 | ||
|
|
0c76995f6d | ||
|
|
17cab8fcb7 | ||
|
|
d64753859d | ||
|
|
48b1c24c95 | ||
|
|
7e5e12b139 | ||
|
|
59ba4752c0 | ||
|
|
189219c673 | ||
|
|
4b54262edd | ||
|
|
458b035a7e | ||
|
|
abe85cf6e7 | ||
|
|
379b63275a | ||
|
|
07cde689b1 | ||
|
|
26606f138d | ||
|
|
24c4774f7e | ||
|
|
6bbb4f577e | ||
|
|
4890de5052 | ||
|
|
746af37899 | ||
|
|
0136ce7034 | ||
|
|
5ca47a91a6 | ||
|
|
d849df41be | ||
|
|
a2269bcdd5 | ||
|
|
d41394c70a | ||
|
|
756f73bbd2 | ||
|
|
95eb17b6d1 | ||
|
|
773fd1f221 | ||
|
|
c95b436680 | ||
|
|
3f47151678 | ||
|
|
dbf1e7136c | ||
|
|
84b44eef91 | ||
|
|
686ada1e49 | ||
|
|
d9f6bc058a | ||
|
|
6f531793f0 | ||
|
|
f8794bef5f | ||
|
|
9daf103669 | ||
|
|
26fa047fe4 | ||
|
|
1a04bb535d | ||
|
|
ad41b7b434 | ||
|
|
6791b1dbdc | ||
|
|
dc1b84130c | ||
|
|
e70a933090 | ||
|
|
08617f2fd4 | ||
|
|
8aefb5db8e | ||
|
|
e1d214b91a | ||
|
|
0285de50e5 | ||
|
|
5f7caa0535 | ||
|
|
df9d2bc472 | ||
|
|
a923b6beca | ||
|
|
760ef77593 | ||
|
|
64f703856e | ||
|
|
36eba36bf2 | ||
|
|
332176a619 | ||
|
|
cd5b60c8cc | ||
|
|
5ea8947fc9 | ||
|
|
474a5c8e42 | ||
|
|
952062d5be | ||
|
|
c071564de4 | ||
|
|
b2fa7563d3 | ||
|
|
22f68fedcb | ||
|
|
0a4cda26cb | ||
|
|
54eb84e690 | ||
|
|
5cd33814fc | ||
|
|
90f19679e7 | ||
|
|
38ade9e498 | ||
|
|
4c77831001 | ||
|
|
722fe556e3 | ||
|
|
98b83c0078 | ||
|
|
85646b5ffa | ||
|
|
d3f5ed7d60 | ||
|
|
e0a8e93704 | ||
|
|
3aab5e9eb7 | ||
|
|
2307ac88cb | ||
|
|
0f2018d913 | ||
|
|
8b9a75ba5a | ||
|
|
7872e0ce38 | ||
|
|
d28ac73987 | ||
|
|
2c8c7a8b51 | ||
|
|
f98fc6bf3c | ||
|
|
d42958734f | ||
|
|
6f2c785713 | ||
|
|
05a1ce51f0 | ||
|
|
58b063770d | ||
|
|
6f9a83709d | ||
|
|
bd2a3ddfba | ||
|
|
4433b6623e | ||
|
|
e1ca17637f | ||
|
|
5ef0e43312 | ||
|
|
aa6ca95f13 | ||
|
|
2523467c97 | ||
|
|
b38eb6709e | ||
|
|
f6dacdb046 | ||
|
|
cc0903f74d | ||
|
|
72936fc0d1 | ||
|
|
4b7f552885 | ||
|
|
9b2fc60d79 | ||
|
|
3e5a26b95a | ||
|
|
a5f3139784 | ||
|
|
9ca9fcdb09 | ||
|
|
913db35b23 | ||
|
|
8eba3ba4f7 | ||
|
|
5bd0f24a84 | ||
|
|
e8fb3180db | ||
|
|
232aaad090 | ||
|
|
b3dbb8d4be | ||
|
|
3caf070056 | ||
|
|
1e24bbc3e2 | ||
|
|
18694fd24a | ||
|
|
10dc003f20 | ||
|
|
809e2496c0 | ||
|
|
c1c37901b5 | ||
|
|
44e115f211 | ||
|
|
217cc338a8 | ||
|
|
81eccc177d | ||
|
|
4ea00b7e7b | ||
|
|
844699263d | ||
|
|
d72a548852 | ||
|
|
722ed9bece | ||
|
|
8852367837 | ||
|
|
5559f9b9af | ||
|
|
39526edafd | ||
|
|
a4f86354d3 | ||
|
|
636204bbaf | ||
|
|
08494834c2 | ||
|
|
2247f34b89 | ||
|
|
5c84412238 | ||
|
|
13112aa4d8 | ||
|
|
836a165ff2 | ||
|
|
f36e0b39ed | ||
|
|
74e2ec74e0 | ||
|
|
bcb6243f3b | ||
|
|
c00b628fb4 | ||
|
|
2fbed17ebc | ||
|
|
7bfea23958 | ||
|
|
91c567779b | ||
|
|
c4aeeb419e | ||
|
|
7808fbb852 | ||
|
|
9e64bd474b | ||
|
|
25a6aef472 | ||
|
|
203ba521b0 | ||
|
|
287685420a | ||
|
|
a6ae0ba4b0 | ||
|
|
208346a480 | ||
|
|
0d5c55a3b5 | ||
|
|
4fcd2bf14c | ||
|
|
deea1aba7f | ||
|
|
3e31666cd6 | ||
|
|
197c4c46ab | ||
|
|
33cb2ed43d | ||
|
|
b56e521c5f | ||
|
|
639b930ac9 | ||
|
|
607235eb01 | ||
|
|
dbfeb4b0fa | ||
|
|
432725b80d | ||
|
|
91c2909c4a | ||
|
|
699a2f7956 | ||
|
|
7c2751ad37 | ||
|
|
50b524fa7a | ||
|
|
aee26bc9b2 | ||
|
|
78de254230 | ||
|
|
22fe4053e9 | ||
|
|
e73f9ac8f2 | ||
|
|
19987234e5 | ||
|
|
33239f28f8 | ||
|
|
350f4572ed | ||
|
|
ab29267d64 | ||
|
|
b410820868 | ||
|
|
b1268e0001 | ||
|
|
9fe661269d | ||
|
|
a715a77e76 | ||
|
|
20933f77f5 | ||
|
|
c618fcafaa | ||
|
|
350ce2ad35 | ||
|
|
eef8580e89 | ||
|
|
406fc05970 | ||
|
|
2b6cb775d8 | ||
|
|
fe27fd4c86 | ||
|
|
784dca2baa | ||
|
|
6cff6414c2 | ||
|
|
b84b6c3464 | ||
|
|
bd3931d62d | ||
|
|
7ababb626b | ||
|
|
62b2833c2f | ||
|
|
190c82bbe6 | ||
|
|
b8055d45e7 | ||
|
|
fe09bb3c91 | ||
|
|
84d0a91c44 | ||
|
|
9d95f660e3 | ||
|
|
2d76d1f50c | ||
|
|
df3b921654 | ||
|
|
e98fccdcfd | ||
|
|
7b9b940b6a | ||
|
|
3a4b750d93 | ||
|
|
40e3555d13 | ||
|
|
4b6adb3919 | ||
|
|
66aece580f | ||
|
|
09fc200d55 | ||
|
|
b17b56dba0 | ||
|
|
a9d0531a56 | ||
|
|
a7a2c85f82 | ||
|
|
83a06fcd2b | ||
|
|
d2d7b22a8f | ||
|
|
92b78ab3c8 | ||
|
|
0bed3bab98 | ||
|
|
a60637fb79 | ||
|
|
f1218264be | ||
|
|
18cf63facc | ||
|
|
9f8e1775af | ||
|
|
0e39f3ad05 | ||
|
|
4fc90c0168 | ||
|
|
57b4f539f9 | ||
|
|
8667ce5fb4 | ||
|
|
0ed922bb29 | ||
|
|
80eff97be3 | ||
|
|
e5cfe5cd99 | ||
|
|
1d8b952a6d | ||
|
|
c204f7a514 | ||
|
|
6e2ef1e29c | ||
|
|
5f0402043a | ||
|
|
6ad7ccdd6e | ||
|
|
90a2fd7f19 | ||
|
|
58c8b11f85 | ||
|
|
4d700be9ae | ||
|
|
da0d02b990 | ||
|
|
854cb59f6b | ||
|
|
33cbee23d4 | ||
|
|
0497cf384d | ||
|
|
acd8fa3810 | ||
|
|
9d024bca3b | ||
|
|
7434c9db55 | ||
|
|
a3ab0b3243 | ||
|
|
64608f6b05 | ||
|
|
a207c982b7 | ||
|
|
072acac399 | ||
|
|
8049466dff | ||
|
|
80077ba94d | ||
|
|
bd4744557f | ||
|
|
d8072634ef | ||
|
|
27158f7075 | ||
|
|
f06f1fb7f1 | ||
|
|
8bb7b75f05 | ||
|
|
0c9e92abb0 | ||
|
|
7a8787b1a0 | ||
|
|
78f71c8b13 | ||
|
|
a0a35e8253 | ||
|
|
e892e3f9e1 | ||
|
|
2dadc98485 | ||
|
|
1bd2b4629e | ||
|
|
ce52cbf6ac | ||
|
|
0abcb5aba1 | ||
|
|
ca7bdc913e | ||
|
|
2de7d34b4a | ||
|
|
0eea78f796 | ||
|
|
95fa7e8130 | ||
|
|
fd78876dd3 | ||
|
|
eaa3075516 | ||
|
|
92338fd219 | ||
|
|
26d32c02ea | ||
|
|
e03efc33d7 | ||
|
|
2d1188af11 | ||
|
|
66666ed5fd | ||
|
|
dfcb283899 | ||
|
|
24215f8b28 | ||
|
|
ddc2b4c2ad | ||
|
|
10cef483f4 | ||
|
|
51dde2f71e | ||
|
|
5e0117e7c6 | ||
|
|
3b3535faec | ||
|
|
3e1067faba | ||
|
|
722382f60e | ||
|
|
e47a512fc8 | ||
|
|
0627359b6e | ||
|
|
01d1e4c7f5 | ||
|
|
3f564e8179 | ||
|
|
b6f3038a73 | ||
|
|
e67354f34c | ||
|
|
a04982425f | ||
|
|
2e6f99edf0 | ||
|
|
9307d08bb6 | ||
|
|
2cdcd98e08 | ||
|
|
7540ee942f | ||
|
|
7bfad03b2e | ||
|
|
9dacd7056b | ||
|
|
4ebe66a29c | ||
|
|
6ebed9eff6 | ||
|
|
cc13685062 | ||
|
|
6b28490d7b | ||
|
|
27ba592434 | ||
|
|
cf5daffe69 | ||
|
|
192c0e529d | ||
|
|
c300e16de9 | ||
|
|
e46eecfc8e | ||
|
|
1e2add4b97 | ||
|
|
975f515691 | ||
|
|
05f8ae2aae | ||
|
|
525b3a0da9 | ||
|
|
03265e03fc | ||
|
|
28c928890a | ||
|
|
6f3e8683a0 | ||
|
|
ba7a16250b | ||
|
|
1cbbce741a | ||
|
|
9f9f6b7a08 | ||
|
|
3ec4823533 | ||
|
|
31d316f5b2 | ||
|
|
8f31d6fcbd | ||
|
|
7b80ea528c | ||
|
|
ee388b01db | ||
|
|
2d4b16db46 | ||
|
|
a7715648d0 | ||
|
|
4ebc4e01cd | ||
|
|
fd0625f2eb | ||
|
|
ef35c53712 | ||
|
|
b5c79be625 | ||
|
|
ee7b9b0cff | ||
|
|
3289405b17 | ||
|
|
b9a30b9ffe | ||
|
|
f606c7c891 | ||
|
|
c76eb01b68 | ||
|
|
34b6e5ebae | ||
|
|
7d771a56e6 | ||
|
|
f1017e345b | ||
|
|
091b61bd10 | ||
|
|
ed9ca764d4 | ||
|
|
70c6480ae9 | ||
|
|
a0ebb3ef58 | ||
|
|
1efb68b11f | ||
|
|
9c564df350 | ||
|
|
767e7650e0 | ||
|
|
f32ed3a5c8 | ||
|
|
7e7c869fc4 | ||
|
|
9da64c34ff | ||
|
|
23266d12ec | ||
|
|
3920d151af | ||
|
|
9551cdf18a | ||
|
|
1ec90895da | ||
|
|
bd67b8bf7f | ||
|
|
53c116309e | ||
|
|
5a53ae0d34 | ||
|
|
207c0e3708 | ||
|
|
7b7d76e977 | ||
|
|
a1d3b5c887 | ||
|
|
796bcb34ad | ||
|
|
9f274308ca | ||
|
|
7feb152b35 | ||
|
|
4fad8b77bf | ||
|
|
2c90c90610 | ||
|
|
9ac66f57fa | ||
|
|
82fcc196df | ||
|
|
c7dbcbf336 | ||
|
|
5591c60c14 | ||
|
|
dcdcf0bbbe | ||
|
|
e899f85101 | ||
|
|
96ef70b943 | ||
|
|
1f24e135d9 | ||
|
|
26d0cb1f3d | ||
|
|
5256e468db | ||
|
|
82ade2a8f3 | ||
|
|
f0d31fd48d | ||
|
|
14de145c02 | ||
|
|
060cc38e84 | ||
|
|
453a9b30e9 | ||
|
|
73d982ebad | ||
|
|
e6ba8e94db | ||
|
|
05f13c61fc | ||
|
|
d52d909171 | ||
|
|
d6c6f39387 | ||
|
|
31946032ba | ||
|
|
609e3d42a1 | ||
|
|
016b7d231a | ||
|
|
7c61961c55 | ||
|
|
89746b3338 | ||
|
|
64ab6a28b3 | ||
|
|
4bf872d292 | ||
|
|
063ceffb11 | ||
|
|
f68ca5ab73 | ||
|
|
eb47b5d08a | ||
|
|
2665ea1203 | ||
|
|
b2dc150f03 | ||
|
|
70ff8ead90 | ||
|
|
0610419e38 | ||
|
|
cd68d1f198 | ||
|
|
091f83ff59 | ||
|
|
8504e9bf74 | ||
|
|
013d1ed056 | ||
|
|
8d7d6eee76 | ||
|
|
cba2ed5424 | ||
|
|
d913ce8e1a | ||
|
|
84e7c4df49 | ||
|
|
71a2f4c284 | ||
|
|
777f49002e | ||
|
|
1e6dfbff96 | ||
|
|
053f414ef6 | ||
|
|
6321564bed | ||
|
|
76be6b8847 | ||
|
|
9205a39d3e | ||
|
|
1471f165de | ||
|
|
e40d785e4e | ||
|
|
b6e2cf645b | ||
|
|
759fb6622d | ||
|
|
7c7ca8d757 | ||
|
|
0f3ff7bdbb | ||
|
|
e0280a56ce | ||
|
|
9da394f711 | ||
|
|
582c39611d | ||
|
|
7d82785ade | ||
|
|
4e8544ce97 | ||
|
|
a8d746e831 | ||
|
|
9d8df566fc | ||
|
|
99156155b3 | ||
|
|
17fe4ac327 | ||
|
|
3b6e6495c5 | ||
|
|
b20a54f7e9 | ||
|
|
3bb4d74fab | ||
|
|
4dd9c06850 | ||
|
|
2ca9322d82 | ||
|
|
5e304a5a6b | ||
|
|
21eb039978 | ||
|
|
6026ac31b1 | ||
|
|
72e11a7d31 | ||
|
|
b0656e70b8 | ||
|
|
83141b3564 | ||
|
|
b25c128051 | ||
|
|
afc9d13d45 | ||
|
|
7e106f3468 | ||
|
|
e74a102700 | ||
|
|
1037a1749b | ||
|
|
8c19622db2 | ||
|
|
0eae201401 | ||
|
|
3de83d1178 | ||
|
|
c17ebddcef | ||
|
|
95fd9f92cd | ||
|
|
fda1a2a728 | ||
|
|
98dc95d105 | ||
|
|
c5da8d154d | ||
|
|
95209af8f9 | ||
|
|
35255fdaee | ||
|
|
cd7ad06a7d | ||
|
|
d0b8cd8221 | ||
|
|
1aac17aeff | ||
|
|
ccf0769f43 | ||
|
|
076e1092f0 | ||
|
|
670df8c0cb | ||
|
|
a817dceb24 | ||
|
|
227bcd509f | ||
|
|
1e6605ecd3 | ||
|
|
84f35f55d4 | ||
|
|
43d1aa17a3 | ||
|
|
fc0958e511 | ||
|
|
bfe3a861f5 | ||
|
|
658ca60f58 | ||
|
|
e4709c599b | ||
|
|
02ffe71d80 | ||
|
|
3ef3084070 | ||
|
|
250fcf4bd6 | ||
|
|
86baabc452 | ||
|
|
6126aa146b | ||
|
|
64ce7fedcd | ||
|
|
6c85af386c | ||
|
|
7ff58c4e6d | ||
|
|
ecef8ddd72 | ||
|
|
2f96e27adb | ||
|
|
10bd7e9485 | ||
|
|
f6fa4c3064 | ||
|
|
64a525133b | ||
|
|
485b9f32df | ||
|
|
dc6859f4f0 | ||
|
|
f861f2ba3f | ||
|
|
3a9b39b94b | ||
|
|
9a5d3461fe | ||
|
|
6add2094a6 | ||
|
|
843e5a9ad4 | ||
|
|
a8d41e289c | ||
|
|
3a591a5574 | ||
|
|
6076b4cde1 | ||
|
|
b158849d07 | ||
|
|
e4d448a661 | ||
|
|
42b51243a7 | ||
|
|
8dd87d0172 | ||
|
|
c92a94bdd2 | ||
|
|
c7ce98f1d5 | ||
|
|
6fb6ac2d6c | ||
|
|
fe539baa34 | ||
|
|
9aa7399484 | ||
|
|
eaa97e7eb8 | ||
|
|
dd7e37ef6e | ||
|
|
64c46118b0 | ||
|
|
bdc5f69ae0 | ||
|
|
35616394bc | ||
|
|
f463e76f5e | ||
|
|
fb7b57210a | ||
|
|
6ccd791574 | ||
|
|
13b12d80da | ||
|
|
571de25e81 | ||
|
|
08c2b02be9 | ||
|
|
6e0a3387cd | ||
|
|
c5f6e1262f | ||
|
|
27212da8df | ||
|
|
9d89d33701 | ||
|
|
5754979918 | ||
|
|
bf66fa386b | ||
|
|
e91338a2e6 | ||
|
|
58616d4927 | ||
|
|
2b2ac20feb | ||
|
|
7c11f8ab37 | ||
|
|
cc7fde9a27 | ||
|
|
ed56a73821 | ||
|
|
7e7fa6af65 | ||
|
|
5d591bca35 | ||
|
|
0b8e15cad1 | ||
|
|
d96b963385 | ||
|
|
671040a3a7 | ||
|
|
429353fa54 | ||
|
|
8003603ffd | ||
|
|
f8e767511b | ||
|
|
6a97980a18 | ||
|
|
2ab0df8e17 | ||
|
|
97a3ca223f | ||
|
|
bee9f61aa9 | ||
|
|
c193eb155e | ||
|
|
cc5ac0c659 | ||
|
|
752d063d5f | ||
|
|
06229d0d1f | ||
|
|
4ac8f5a354 | ||
|
|
6628715e40 | ||
|
|
d84dc0dd48 | ||
|
|
253e2a58be | ||
|
|
5f8daf56b1 | ||
|
|
cdd5aedadd | ||
|
|
52809a01d8 | ||
|
|
b459413c0e | ||
|
|
b147c4faaa | ||
|
|
e63ccb0541 | ||
|
|
ea878ca627 | ||
|
|
2dc2a998ce | ||
|
|
f747e01285 | ||
|
|
d9a53805ee | ||
|
|
9c3fc0dede | ||
|
|
cc409fbab6 | ||
|
|
8de71ff2d3 | ||
|
|
457768bb10 | ||
|
|
046b89efc5 | ||
|
|
e7f7772a74 | ||
|
|
0082ebfdb1 | ||
|
|
a9f0c41cae | ||
|
|
30b16984ed | ||
|
|
c8ce379163 | ||
|
|
f5bfb0255c | ||
|
|
f2b92cc6e0 | ||
|
|
6a97f39f66 | ||
|
|
f389ff701a | ||
|
|
648df02624 | ||
|
|
4cee98bcce | ||
|
|
7e33dc7e25 | ||
|
|
6f68fbfd6d | ||
|
|
7fe3c2b285 | ||
|
|
a07ba498d6 | ||
|
|
d7222c8e31 | ||
|
|
9426049095 | ||
|
|
edec8788c3 | ||
|
|
1a66f52f75 | ||
|
|
ccab152fe3 | ||
|
|
e2e5ef7a93 | ||
|
|
c9b6b74020 | ||
|
|
e353f9c295 | ||
|
|
f2a24d0713 | ||
|
|
8423c124d7 | ||
|
|
a9dec9fe4d | ||
|
|
343d3aaaf2 | ||
|
|
1bec261878 | ||
|
|
33cffb015e | ||
|
|
b5da0c6043 | ||
|
|
dfc24c3c33 | ||
|
|
12fb1e7891 | ||
|
|
781f8d7961 | ||
|
|
895cb65c10 | ||
|
|
fb1196785d | ||
|
|
4c8ac48af0 | ||
|
|
e8d0b39b5d | ||
|
|
6dbcd55727 | ||
|
|
40f5dfeb42 | ||
|
|
c5b96954bf | ||
|
|
debd8afe31 | ||
|
|
46dc47727b | ||
|
|
7a2380ffe8 | ||
|
|
ca1f4ba227 | ||
|
|
dbf45502d1 | ||
|
|
44892d9401 | ||
|
|
82b32f8b65 | ||
|
|
6026422056 | ||
|
|
69ff39a46c | ||
|
|
f90a51bf40 | ||
|
|
9ea885d908 | ||
|
|
ece494e897 | ||
|
|
85ca35684c | ||
|
|
8d34adee14 | ||
|
|
3a74c9483e | ||
|
|
727caf3f4f | ||
|
|
eca5967b4d | ||
|
|
7dce65100a | ||
|
|
40d78ab69a | ||
|
|
9196caec97 | ||
|
|
5fa6e32bbd | ||
|
|
bb5c65cf4e | ||
|
|
f512bc0870 | ||
|
|
b3b8de1c4c | ||
|
|
b4b5eb9266 | ||
|
|
9ddd280271 | ||
|
|
b5f16ee34d | ||
|
|
bf0511efa4 | ||
|
|
236d3c7f33 | ||
|
|
7fd4b1d0f5 | ||
|
|
8825a18b22 | ||
|
|
df9dc477ed | ||
|
|
8c2b6b2bc0 | ||
|
|
512e91e2bc | ||
|
|
812c51804b | ||
|
|
93185149b3 | ||
|
|
6298f31a91 | ||
|
|
49af66fb4f | ||
|
|
abcd1c54b4 | ||
|
|
d2eaf0b38c | ||
|
|
f0957c615e | ||
|
|
ce5d5d13a3 |
@@ -0,0 +1,761 @@
|
||||
### 功能描述以及必要性描述
|
||||
|
||||
---
|
||||
name: gin-vue-admin
|
||||
description: |
|
||||
gin-vue-admin 是一个基于现代化技术栈的全栈管理系统框架。
|
||||
|
||||
前端技术栈:
|
||||
- Vue 3.5.7 + Composition API
|
||||
- Vite 6.2.3 构建工具
|
||||
- Pinia 2.2.2 状态管理
|
||||
- Element Plus 2.10.2 UI组件库
|
||||
- UnoCSS 66.4.2 原子化CSS框架
|
||||
- Vue Router 4.4.3 路由管理
|
||||
- Axios 1.8.2 HTTP客户端
|
||||
- ECharts 5.5.1 数据可视化
|
||||
- @vueuse/core Vue组合式API工具集
|
||||
|
||||
后端技术栈:
|
||||
- Go 1.23 + Gin 1.10.0 Web框架
|
||||
- GORM 1.25.12 ORM框架
|
||||
- Casbin 2.103.0 权限管理
|
||||
- Viper 1.19.0 配置管理
|
||||
- Zap 1.27.0 日志系统
|
||||
- Redis 9.7.0 缓存
|
||||
- JWT 5.2.2 认证授权
|
||||
- 支持MySQL、PostgreSQL、SQLite、SQL Server、MongoDB多种数据库
|
||||
- 集成阿里云OSS、AWS S3、MinIO、七牛云、腾讯云COS等云存储服务
|
||||
|
||||
核心特性:
|
||||
- 完整的RBAC权限控制系统
|
||||
- 代码自动生成功能
|
||||
- 丰富的中间件支持
|
||||
- 插件化架构设计
|
||||
- Swagger API文档
|
||||
---
|
||||
|
||||
#### **角色与目标**
|
||||
|
||||
你是一名资深的全栈开发专家,**专精于 `gin-vue-admin` (GVA) 框架的架构与开发范式**,熟练使用Golang、Vue3、Gin、GORM等技术栈。
|
||||
|
||||
你的核心任务是,根据需求开发**完整、生产级别的全栈功能包或插件**。你必须严格遵循 GVA 的分层架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
|
||||
|
||||
---
|
||||
|
||||
### **🚀 重要提示:GVA Helper MCP 支持**
|
||||
|
||||
**在开始任何GVA开发工作之前,请务必注意以下重要工作流程:**
|
||||
|
||||
1. **MCP支持**: GVA框架本身支持MCP(Model Context Protocol),提供了强大的开发辅助能力
|
||||
|
||||
2. **GVA Helper**: 通常会有一个名为 "**GVA Helper**" 的MCP助手,专门为GVA框架开发提供支持
|
||||
|
||||
3. **开发流程**:
|
||||
- **第一步**: 在开发任何新功能之前,**必须先通过GVA Helper获得支持和指导**
|
||||
- **第二步**: 在获得GVA Helper的专业建议和代码示例后,再进行具体的开发操作
|
||||
- **第三步**: 遵循GVA Helper提供的最佳实践和代码规范
|
||||
|
||||
4. **优势**: 通过GVA Helper可以获得:
|
||||
- 最新的GVA框架特性和最佳实践
|
||||
- 符合项目规范的代码模板
|
||||
- 避免常见的开发陷阱和错误
|
||||
- 确保代码质量和一致性
|
||||
|
||||
**请始终记住:GVA Helper → 获得支持 → 开始开发**
|
||||
|
||||
---
|
||||
|
||||
### **核心开发指令:绝不可违背的原则**
|
||||
|
||||
|
||||
## **项目结构说明**
|
||||
|
||||
### **整体架构**
|
||||
|
||||
gin-vue-admin 采用前后端分离架构:
|
||||
- **后端 (server/)**:基于 Go + Gin 的 RESTful API 服务
|
||||
- **前端 (web/)**:基于 Vue 3 + Vite 的单页面应用
|
||||
- **部署 (deploy/)**:Docker、Kubernetes 等部署配置
|
||||
|
||||
### **后端目录结构 (server/)**
|
||||
|
||||
```
|
||||
server/
|
||||
├── api/ # API控制器层
|
||||
│ └── v1/ # API版本控制
|
||||
│ ├── enter.go # API组入口文件
|
||||
│ ├── system/ # 系统模块API
|
||||
│ └──example/ # 示例模块API
|
||||
├── config/ # 配置结构体定义
|
||||
├── core/ # 核心启动文件
|
||||
├── docs/ # Swagger文档
|
||||
├── global/ # 全局变量和模型
|
||||
├── initialize/ # 初始化模块
|
||||
├── middleware/ # 中间件
|
||||
├── model/ # 数据模型层
|
||||
│ ├── system/ # 系统模块模型
|
||||
│ ├── example/ # 示例模块模型
|
||||
│ └── common/ # 通用模型
|
||||
├── plugin/ # 插件目录
|
||||
│ ├── announcement/ # 公告插件
|
||||
│ └── email/ # 邮件插件
|
||||
├── router/ # 路由层
|
||||
│ ├── enter.go # 路由组入口
|
||||
│ ├── system/ # 系统路由
|
||||
│ └──example/ # 示例路由
|
||||
├── service/ # 服务层
|
||||
│ ├── enter.go # 服务组入口
|
||||
│ ├── system/ # 系统服务
|
||||
│ └── example/ # 示例服务
|
||||
├── source/ # 数据初始化
|
||||
├── utils/ # 工具包
|
||||
├── config.yaml # 配置文件
|
||||
└── main.go # 程序入口
|
||||
```
|
||||
|
||||
### **前端目录结构 (web/)**
|
||||
|
||||
```
|
||||
web/
|
||||
├── public/ # 静态资源
|
||||
├── src/
|
||||
│ ├── api/ # API接口定义
|
||||
│ │ ├── user.js # 用户相关API
|
||||
│ │ ├── menu.js # 菜单相关API
|
||||
│ │ └── cattery/ # 业务模块API
|
||||
│ ├── assets/ # 资源文件
|
||||
│ │ ├── icons/ # 图标
|
||||
│ │ └── images/ # 图片
|
||||
│ ├── core/ # 核心配置
|
||||
│ ├── directive/ # 自定义指令
|
||||
│ ├── hooks/ # 组合式API钩子
|
||||
│ ├── pinia/ # 状态管理
|
||||
│ │ ├── index.js # Pinia入口
|
||||
│ │ └── modules/ # 状态模块
|
||||
│ ├── plugin/ # 前端插件
|
||||
│ │ ├── announcement/ # 公告插件
|
||||
│ │ └── email/ # 邮件插件
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── style/ # 样式文件
|
||||
│ ├── utils/ # 工具函数
|
||||
│ ├── view/ # 页面组件
|
||||
│ │ ├── dashboard/ # 仪表盘
|
||||
│ │ ├── layout/ # 布局组件
|
||||
│ │ ├── login/ # 登录页
|
||||
│ │ ├── superAdmin/ # 超级管理员
|
||||
│ │ ├── systemTools/ # 系统工具
|
||||
│ │ └── cattery/ # 业务页面
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.js # 程序入口
|
||||
├── package.json # 依赖配置
|
||||
├── vite.config.js # Vite配置
|
||||
└── uno.config.js # UnoCSS配置
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 后端规则
|
||||
|
||||
在编写任何代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
|
||||
|
||||
1. **严格的分层架构**:
|
||||
|
||||
- **职责单一**: 每个层(Model, Service, API, Router)都有其唯一职责,**严禁跨层调用**。例如,API层绝不能直接操作数据库,必须通过Service层。Service层绝不能直接处理`gin.Context`。
|
||||
|
||||
- **依赖关系**: 依赖链条必须是单向的:`Router -> API -> Service -> Model`。
|
||||
|
||||
2. **`enter.go` 组管理模式**:
|
||||
|
||||
- 所有 `api`, `service`, `router` 层都**必须**使用 `enter.go` 文件来创建和暴露各自的 `ApiGroup`, `ServiceGroup`, `RouterGroup`。
|
||||
|
||||
- 全局实例变量(如 `service.ServiceGroupApp`)是模块间通信的唯一入口,以此来避免循环引用。
|
||||
|
||||
3. **详尽的 Swagger 注释 (API层强制要求)**:
|
||||
|
||||
- **每一个**对外暴露的 API 函数都**必须**拥有完整且准确的 Swagger 注释块。这不仅是API文档的来源,也是前后端协作、自动化测试和前端AI分析的基础。注释必须清晰地描述接口的功能、参数和返回值。
|
||||
|
||||
4. **统一的响应与错误处理**:
|
||||
|
||||
- Service 层函数遇到业务错误时,应返回 `error` 对象。
|
||||
|
||||
- API 层负责捕获 Service 层的 `error`,并使用项目统一的 `response` 包(如 `response.OkWithDetailed` 或 `response.FailWithMessage`)将其转换为格式化的 JSON 响应和正确的 HTTP 状态码。
|
||||
|
||||
|
||||
---
|
||||
|
||||
### **各层级代码实现规范**
|
||||
|
||||
#### **1. 模型层 (`model/`)**
|
||||
|
||||
- **数据模型 (`model/xxx.go`)**:
|
||||
|
||||
- 用于定义与数据库表映射的 GORM 结构体。
|
||||
|
||||
- 结构体应继承 `global.GVA_MODEL` 以包含 `ID`, `CreatedAt`, `UpdatedAt` 等基础字段。
|
||||
|
||||
- 必须为字段添加清晰的 `json` 和 `gorm` 标签。
|
||||
|
||||
- **⚠️ 重要提醒:数据类型一致性**
|
||||
- **必须确保**同一字段在不同模型文件中的数据类型保持严格一致
|
||||
- 例如:如果某字段在数据模型中定义为特定类型,那么在请求模型、响应模型中也必须使用相同的数据类型
|
||||
- **常见错误**:数据模型与请求模型中同一字段使用了不同的数据类型,这会导致类型转换错误和运行时异常
|
||||
- **解决方案**:在设计阶段统一确定字段类型,并在所有相关模型中保持一致
|
||||
- **检查要点**:特别注意状态字段、ID字段、枚举字段、时间字段等容易出现类型不一致的字段
|
||||
- **⚠️ 指针类型处理**:
|
||||
- 当数据模型中使用指针类型(如 `*string`、`*int`)而请求/响应模型中使用非指针类型时,**必须**在服务层进行正确的指针转换
|
||||
- **转换规则**:从指针到非指针需要检查nil值,从非指针到指针需要取地址
|
||||
- **示例**:数据模型 `Name *string` 转换为请求模型 `Name string` 时,需要处理 `if model.Name != nil { request.Name = *model.Name }`
|
||||
|
||||
- **请求模型 (`model/request/xxx.go`)**:
|
||||
|
||||
- 用于定义接收前端请求参数的结构体(DTOs)。
|
||||
|
||||
- **必须**为字段添加 `json` 和 `form` 标签,以便 Gin 进行参数绑定。
|
||||
|
||||
- 对于列表查询请求,应创建一个 `XxxSearch` 结构体,并内嵌通用的 `request.PageInfo` 分页结构体。
|
||||
|
||||
|
||||
#### **2. 服务层 (`service/`)**
|
||||
|
||||
- **职责**: 封装所有核心业务逻辑,进行数据库的CRUD操作。**此层不应出现任何与HTTP协议相关的代码(如 `gin.Context`)**。
|
||||
|
||||
- **结构**: 在 `service/` 下为每个模块创建 `xxx_service.go` 文件,并在 `service/enter.go` 中注册。
|
||||
|
||||
- **函数签名**: 函数应接收具体的业务参数(如 `model.Xxx` 或 `request.XxxSearch`),并返回处理结果和 `error`。
|
||||
|
||||
- **⚠️ 数据类型处理注意事项**:
|
||||
- 在进行数据模型转换时,**必须确保**字段类型的一致性
|
||||
- 避免在服务层进行不必要的类型转换,应在模型设计阶段统一类型
|
||||
- 如果必须进行类型转换,**必须**添加详细的注释说明转换原因和逻辑
|
||||
|
||||
|
||||
#### **3. API层 (`api/`)**
|
||||
|
||||
- **职责**: 作为HTTP请求的入口,负责参数校验、调用Service层方法、并返回格式化的JSON响应。
|
||||
|
||||
- **结构**: 在 `api/` 下为每个模块创建 `xxx_api.go` 文件,并在 `api/enter.go` 中注册。
|
||||
|
||||
- **交互**: **必须**通过全局变量 `service.ServiceGroupApp` 来调用服务层的方法。
|
||||
|
||||
- **Swagger 示例 (必须遵循)**:
|
||||
|
||||
Go
|
||||
|
||||
```
|
||||
// CreateXxx 创建XXX
|
||||
// @Tags XxxModule
|
||||
// @Summary 创建一个新的XXX
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CreateXxxRequest true "XXX的名称和描述"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /xxx/createXxx [post]
|
||||
func (a *XxxApi) CreateXxx(c *gin.Context) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### **4. 路由层 (`router/`)**
|
||||
|
||||
- **职责**: 定义API路由规则,并将HTTP请求路径映射到具体的API处理函数上,同时配置中间件。
|
||||
|
||||
- **结构**: 在 `router/` 下为每个模块创建 `xxx_router.go` 文件,并在 `router/enter.go` 中注册。
|
||||
|
||||
- **交互**: **必须**通过全局变量 `api.ApiGroupApp` 来引用API层的处理函数。
|
||||
|
||||
- **路由分组**: 应根据业务需求和权限,合理使用路由组 (`Router.Group()`),并挂载不同的中间件(如鉴权、操作记录等)。
|
||||
|
||||
#### **5. 初始化层 (`initialize/`)**
|
||||
|
||||
- **职责**: 提供插件资源(数据库、路由、菜单等)的初始化入口,供主程序调用。
|
||||
|
||||
- **`gorm.go`**: 实现 `InitializeDB` 函数,**必须**调用 `db.AutoMigrate` 自动迁移本插件所有 `model` 的表结构。
|
||||
|
||||
- **`router.go`**: 实现 `InitializeRouter` 函数,**必须**调用 `router.RouterGroupApp` 中本插件路由的初始化方法,注册所有API路由。
|
||||
|
||||
- **`menu.go`**: 实现 `InitializeMenu` 函数,负责在数据库中创建或更新本插件的侧边栏菜单、按钮和对应的API权限。
|
||||
- viper.go: 加载插件配置文件
|
||||
- api.go: 注册API到系统
|
||||
|
||||
|
||||
#### **6. 插件入口 (`plugin.go`)
|
||||
|
||||
- **职责**: 作为插件的唯一入口,实现 GVA 的插件接口,让框架能够识别和加载本插件。
|
||||
|
||||
- **接口实现**: **必须**定义一个结构体并实现 `system.Plugin` 接口。
|
||||
|
||||
- **`Register`方法**: 实现 `Register` 方法,该方法接收一个 `*gin.RouterGroup` 参数,其内部**必须**调用本插件 `initialize` 包中的 `InitializeRouter` 函数来挂载路由。
|
||||
|
||||
- **`RouterPath`方法**: 实现 `RouterPath` 方法,返回该插件所有API的根路径,例如 `"/myPlugin"`。
|
||||
|
||||
### 模块间引用关系:
|
||||
- API层引用Service层:在API文件中定义变量如 `var xxxService = service.ServiceGroupApp.XxxService`
|
||||
- Router层引用API层:在路由函数中使用 `api.ApiGroupApp.XxxApi.XxxMethod`
|
||||
- Initialize/Router引用Router层:通过 `router.RouterGroupApp.XxxRouter.InitXxxRouter`
|
||||
- 各模块通过enter.go文件组织和暴露功能,避免循环引用
|
||||
|
||||
### 代码组织示例:
|
||||
|
||||
1. Service入口 (service/enter.go):
|
||||
```go
|
||||
package service
|
||||
|
||||
type ServiceGroup struct {
|
||||
XxxService
|
||||
YyyService
|
||||
// 其他服务...
|
||||
}
|
||||
|
||||
var ServiceGroupApp = new(ServiceGroup)
|
||||
```
|
||||
|
||||
2. API入口 (api/enter.go):
|
||||
```go
|
||||
package api
|
||||
|
||||
type ApiGroup struct {
|
||||
XxxApi
|
||||
YyyApi
|
||||
// 其他API...
|
||||
}
|
||||
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
```
|
||||
|
||||
3. Router入口 (router/enter.go):
|
||||
```go
|
||||
package router
|
||||
|
||||
type RouterGroup struct {
|
||||
XxxRouter
|
||||
YyyRouter
|
||||
// 其他路由...
|
||||
}
|
||||
|
||||
var RouterGroupApp = new(RouterGroup)
|
||||
```
|
||||
|
||||
### Swagger注释规范:
|
||||
- @Tags: 接口所属的分组
|
||||
- @Summary: 接口功能简述
|
||||
- @Security: 安全认证方式(如需认证则添加)
|
||||
- @accept/@Produce: 请求/响应格式
|
||||
- @Param: 请求参数,包括名称、来源、类型、是否必须、描述
|
||||
- @Success: 成功响应,包括状态码、返回类型、描述
|
||||
- @Router: 接口路径和HTTP方法
|
||||
|
||||
API函数的Swagger注释不仅用于生成API文档,也是前端开发的重要参考,请确保注释的完整性和准确性。
|
||||
|
||||
|
||||
---
|
||||
|
||||
### **开发工作流**
|
||||
|
||||
1. **接收任务**: 我会向你下达一个具体的功能插件开发任务,例如:“请为项目创建一个‘商品管理 (Product)’插件”。
|
||||
|
||||
2. **【第一步】模型设计 (奠定基础)**:
|
||||
|
||||
- 你的**首要行动**是分析需求,设计并提供 `model` 和 `model/request` 下的所有 Go 结构体定义。这是后续所有开发的基础。
|
||||
|
||||
3. **【第二步】自下而上,分层实现**:
|
||||
- 具体项目结构可以参考:server/plugin/announcement 这个插件,非常经典!
|
||||
|
||||
- 在模型确认后,你将按照 `Service -> API -> Router` 的顺序,逐层生成代码。
|
||||
|
||||
- 确保每一层的代码都完整、健壮,并严格遵守上述规范。
|
||||
|
||||
4. **【第三步】插件初始化与注册**:
|
||||
|
||||
- 在完成核心功能层的代码后,你将生成 `initialize/` 目录下的相关初始化文件(如 `db.go`, `router.go`)以及插件的主入口文件 `plugin.go`。
|
||||
|
||||
5. **【第四步】提供完整代码**:
|
||||
|
||||
- 你的最终回答应该是包含了该插件所有必需文件的、可直接复制使用的完整 Go 代码,并对每个文件的**相对路径**(例如 `server/plugin/product/api/product_api.go`)和用途进行清晰的说明。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## **前端开发规范**
|
||||
|
||||
### **角色与目标**
|
||||
|
||||
你是一名资深的 Vue.js 前端开发专家,**专精于 `gin-vue-admin` (GVA) 框架的前端架构与开发范式**。
|
||||
|
||||
你的核心任务是,根据需求开发**完整、生产级别的前端功能模块或插件**。你必须严格遵循 GVA 的前端架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
|
||||
|
||||
### **核心开发指令:绝不可违背的原则**
|
||||
|
||||
#### 前端规则
|
||||
|
||||
在编写任何前端代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
|
||||
|
||||
1. **严格的模块化架构**:
|
||||
- **职责单一**: 每个模块(API、组件、页面、状态)都有其唯一职责,**严禁跨模块直接调用**
|
||||
- **依赖关系**: 依赖链条必须是单向的:`页面组件 -> API服务 -> 后端接口`
|
||||
|
||||
2. **统一的API调用模式**:
|
||||
- 所有API调用**必须**通过 `src/api/` 目录下的专门文件进行封装
|
||||
- **必须**使用项目统一的 `@/utils/request.js` 进行HTTP请求
|
||||
- API函数**必须**包含完整的JSDoc注释,描述接口功能、参数和返回值
|
||||
|
||||
3. **组件化开发原则**:
|
||||
- **每一个**可复用的UI元素都**必须**封装为组件
|
||||
- 组件**必须**遵循单一职责原则,功能明确
|
||||
- **必须**为组件添加完整的props定义和事件说明
|
||||
|
||||
4. **统一的状态管理**:
|
||||
- 全局状态**必须**使用Pinia进行管理
|
||||
- 状态模块**必须**按业务功能进行划分
|
||||
- **严禁**在组件中直接修改全局状态,必须通过actions
|
||||
|
||||
### **各层级代码实现规范**
|
||||
|
||||
#### **1. API层 (`src/api/`)**
|
||||
|
||||
- **职责**: 封装所有后端API调用,提供统一的接口服务
|
||||
- **结构**: 按业务模块创建API文件,如 `user.js`、`menu.js`
|
||||
- **规范**:
|
||||
```javascript
|
||||
import service from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* @param {Object} data 查询参数
|
||||
* @param {number} data.page 页码
|
||||
* @param {number} data.pageSize 每页数量
|
||||
* @returns {Promise} 用户列表数据
|
||||
*/
|
||||
export const getUserList = (data) => {
|
||||
return service({
|
||||
url: '/user/getUserList',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
#### **2. 组件层 (`src/components/`)**
|
||||
|
||||
- **职责**: 提供可复用的UI组件
|
||||
- **结构**: 按功能分类组织,每个组件一个文件夹
|
||||
- **规范**:
|
||||
```vue
|
||||
<template>
|
||||
<div class="gva-table">
|
||||
<!-- 组件内容 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 通用表格组件
|
||||
* @component GvaTable
|
||||
* @description 提供统一的表格展示功能
|
||||
*/
|
||||
|
||||
// Props定义
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
// 事件定义
|
||||
const emit = defineEmits(['refresh', 'edit', 'delete'])
|
||||
</script>
|
||||
```
|
||||
|
||||
#### **3. 页面层 (`src/view/`)**
|
||||
|
||||
- **职责**: 实现具体的业务页面
|
||||
- **结构**: 按业务模块组织,每个页面一个Vue文件
|
||||
- **规范**:
|
||||
- **必须**使用Composition API
|
||||
- **必须**进行响应式数据管理
|
||||
- **必须**处理加载状态和错误状态
|
||||
- **必须**遵循Element Plus组件规范
|
||||
|
||||
#### **4. 状态管理 (`src/pinia/`)**
|
||||
|
||||
- **职责**: 管理全局状态和业务逻辑
|
||||
- **结构**: 按业务模块创建store文件
|
||||
- **规范**:
|
||||
```javascript
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
// 状态定义 - 使用 ref() 创建响应式状态
|
||||
const userInfo = ref({
|
||||
uuid: '',
|
||||
nickName: '',
|
||||
headerImg: '',
|
||||
authority: {}
|
||||
})
|
||||
const token = useStorage('token', '')
|
||||
|
||||
// 计算属性 - 使用 computed() 定义
|
||||
const isLogin = computed(() => !!token.value)
|
||||
|
||||
// 方法定义 - 直接定义函数作为 actions
|
||||
const setUserInfo = (val) => {
|
||||
userInfo.value = val
|
||||
}
|
||||
|
||||
const setToken = (val) => {
|
||||
token.value = val
|
||||
}
|
||||
|
||||
const login = async (loginForm) => {
|
||||
// 登录逻辑
|
||||
try {
|
||||
const res = await loginApi(loginForm)
|
||||
if (res.code === 0) {
|
||||
setUserInfo(res.data.user)
|
||||
setToken(res.data.token)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} catch (error) {
|
||||
console.error('Login error:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
// 登出逻辑
|
||||
token.value = ''
|
||||
userInfo.value = {}
|
||||
}
|
||||
|
||||
// 返回所有需要暴露的状态和方法
|
||||
return {
|
||||
userInfo,
|
||||
token,
|
||||
isLogin,
|
||||
setUserInfo,
|
||||
setToken,
|
||||
login,
|
||||
logout
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### **5. 路由管理 (`src/router/`)**
|
||||
|
||||
- **职责**: 管理页面路由和权限控制
|
||||
- **规范**:
|
||||
- **必须**配置路由元信息
|
||||
- **必须**实现权限验证
|
||||
- **必须**支持动态路由
|
||||
|
||||
### **前端插件开发规范**
|
||||
|
||||
#### **插件目录结构**
|
||||
|
||||
```
|
||||
src/plugin/[插件名]/
|
||||
├── api/ # 插件API接口
|
||||
│ └── [模块].js
|
||||
├── components/ # 插件组件(可选)
|
||||
│ └── [组件名].vue
|
||||
├── view/ # 插件页面
|
||||
│ └── [页面名].vue
|
||||
├── form/ # 插件表单(可选)
|
||||
│ └── [表单名].vue
|
||||
└── index.js # 插件入口文件(可选)
|
||||
```
|
||||
|
||||
#### **插件开发原则**
|
||||
|
||||
1. **独立性**: 插件应该是自包含的,不依赖其他业务模块
|
||||
2. **可配置性**: 插件应该支持配置化,便于定制
|
||||
3. **可扩展性**: 插件应该预留扩展接口
|
||||
4. **一致性**: 插件UI风格应与主系统保持一致
|
||||
|
||||
### **代码质量要求**
|
||||
|
||||
1. **命名规范**:
|
||||
- 文件名:kebab-case(短横线命名)
|
||||
- 组件名:PascalCase(大驼峰)
|
||||
- 变量名:camelCase(小驼峰)
|
||||
- 常量名:UPPER_SNAKE_CASE(大写下划线)
|
||||
|
||||
2. **注释规范**:
|
||||
- **必须**为所有API函数添加JSDoc注释
|
||||
- **必须**为复杂组件添加功能说明
|
||||
- **必须**为关键业务逻辑添加行内注释
|
||||
|
||||
3. **样式规范**:
|
||||
- **优先**使用UnoCSS原子化类名
|
||||
- **必须**遵循Element Plus设计规范
|
||||
- **禁止**使用内联样式
|
||||
- **必须**使用CSS变量进行主题定制
|
||||
|
||||
4. **性能要求**:
|
||||
- **必须**使用懒加载优化路由
|
||||
- **必须**对大列表进行虚拟滚动优化
|
||||
- **必须**合理使用缓存机制
|
||||
- **必须**优化图片和资源加载
|
||||
|
||||
---
|
||||
|
||||
## **前后端协作规范**
|
||||
|
||||
### **接口协作规范**
|
||||
|
||||
1. **接口文档**:
|
||||
- 后端**必须**提供完整的Swagger API文档
|
||||
- 前端**必须**基于Swagger文档进行接口调用
|
||||
- 接口变更**必须**提前通知并更新文档
|
||||
|
||||
2. **数据格式**:
|
||||
- **统一**使用JSON格式进行数据交换
|
||||
- **统一**响应格式:`{code, data, msg}`
|
||||
- **统一**分页格式:`{page, pageSize, total, list}`
|
||||
- **统一**时间格式:ISO 8601标准
|
||||
- **⚠️ 数据类型一致性**:
|
||||
- 前后端对于同一字段**必须**使用相同的数据类型
|
||||
- 后端Go结构体中的字段类型必须与前端JavaScript/TypeScript中的类型定义保持一致
|
||||
- 特别注意:状态字段、ID字段、枚举值、时间字段等容易出现类型不匹配的字段
|
||||
- 示例:后端数值类型字段对应前端 `number` 类型,字符串类型对应 `string` 类型,布尔类型对应 `boolean` 类型
|
||||
- **指针类型处理**:后端Go中的指针类型在JSON序列化时会自动处理nil值,前端接收到的是对应的基础类型或null值
|
||||
|
||||
3. **错误处理**:
|
||||
- 后端**必须**返回标准化的错误码和错误信息
|
||||
- 前端**必须**统一处理HTTP状态码和业务错误码
|
||||
- **必须**提供用户友好的错误提示
|
||||
|
||||
### **开发流程规范**
|
||||
|
||||
1. **需求分析阶段**:
|
||||
- 确定功能需求和接口设计
|
||||
- 定义数据模型和业务流程
|
||||
- 制定前后端开发计划
|
||||
|
||||
2. **开发阶段**:
|
||||
- 后端优先开发API接口
|
||||
- 前端基于Mock数据进行并行开发
|
||||
- 定期进行接口联调测试
|
||||
|
||||
3. **测试阶段**:
|
||||
- 单元测试:前后端各自负责
|
||||
- 集成测试:前后端协作完成
|
||||
- 用户验收测试:产品团队主导
|
||||
|
||||
### **版本管理规范**
|
||||
|
||||
1. **分支策略**:
|
||||
- `main`:生产环境分支
|
||||
- `develop`:开发环境分支
|
||||
- `feature/*`:功能开发分支
|
||||
- `hotfix/*`:紧急修复分支
|
||||
|
||||
2. **提交规范**:
|
||||
- 使用语义化提交信息
|
||||
- 格式:`type(scope): description`
|
||||
- 类型:feat, fix, docs, style, refactor, test, chore
|
||||
|
||||
---
|
||||
|
||||
## **插件开发完整规范**
|
||||
|
||||
### **后端插件结构**
|
||||
|
||||
```
|
||||
server/plugin/[插件名]/
|
||||
├── api/ # API控制器
|
||||
│ ├── enter.go # API组入口
|
||||
│ └── [模块].go # 具体API实现
|
||||
├── config/ # 插件配置
|
||||
│ └── config.go
|
||||
├── initialize/ # 初始化模块
|
||||
│ ├── api.go # API注册
|
||||
│ ├── gorm.go # 数据库初始化
|
||||
│ ├── menu.go # 菜单初始化
|
||||
│ ├── router.go # 路由初始化
|
||||
│ └── viper.go # 配置初始化
|
||||
├── model/ # 数据模型
|
||||
│ ├── [模型].go # 数据库模型
|
||||
│ └── request/ # 请求模型
|
||||
├── router/ # 路由定义
|
||||
│ ├── enter.go # 路由组入口
|
||||
│ └── [模块].go # 具体路由
|
||||
├── service/ # 业务服务
|
||||
│ ├── enter.go # 服务组入口
|
||||
│ └── [模块].go # 具体服务
|
||||
└── plugin.go # 插件入口
|
||||
```
|
||||
|
||||
### **前端插件结构**
|
||||
|
||||
```
|
||||
web/src/plugin/[插件名]/
|
||||
├── api/ # API接口
|
||||
│ └── [模块].js
|
||||
├── components/ # 插件组件
|
||||
│ └── [组件].vue
|
||||
├── view/ # 插件页面
|
||||
│ └── [页面].vue
|
||||
├── form/ # 表单组件
|
||||
│ └── [表单].vue
|
||||
└── config.js # 插件配置
|
||||
```
|
||||
|
||||
### **插件开发工作流**
|
||||
|
||||
1. **【第一步】需求分析**:
|
||||
- 明确插件功能和业务需求
|
||||
- 设计数据模型和接口规范
|
||||
- 规划前端页面和交互流程
|
||||
|
||||
2. **【第二步】后端开发**:
|
||||
- 创建数据模型和请求模型
|
||||
- 实现服务层业务逻辑
|
||||
- 开发API控制器和路由
|
||||
- 编写初始化和配置代码
|
||||
|
||||
3. **【第三步】前端开发**:
|
||||
- 创建API接口封装
|
||||
- 开发页面组件和表单
|
||||
- 实现业务逻辑和状态管理
|
||||
- 集成到主系统菜单
|
||||
|
||||
4. **【第四步】测试集成**:
|
||||
- 单元测试和集成测试
|
||||
- 前后端联调测试
|
||||
- 用户体验测试
|
||||
- 性能和安全测试
|
||||
|
||||
### **插件质量标准**
|
||||
|
||||
1. **功能完整性**: 插件功能完整,满足业务需求
|
||||
2. **代码质量**: 代码规范,注释完整,易于维护
|
||||
3. **数据类型一致性**: 前后端数据模型字段类型保持严格一致,避免类型转换错误
|
||||
4. **性能表现**: 响应速度快,资源占用合理
|
||||
5. **用户体验**: 界面友好,操作流畅,错误处理完善
|
||||
6. **兼容性**: 与主系统兼容,不影响其他功能
|
||||
7. **安全性**: 数据安全,权限控制,防止安全漏洞
|
||||
|
||||
---
|
||||
|
||||
### **建议和方案**
|
||||
|
||||
基于以上规范,建议AI在开发gin-vue-admin项目时:
|
||||
|
||||
1. **严格遵循分层架构**:确保前后端代码都按照规定的层次结构组织
|
||||
2. **保持代码一致性**:使用统一的命名规范、注释格式和代码风格
|
||||
3. **注重文档完整性**:确保API文档、代码注释和使用说明的完整性
|
||||
4. **优化用户体验**:关注页面加载速度、交互流畅性和错误处理
|
||||
5. **考虑扩展性**:设计时预留扩展接口,便于后续功能增强
|
||||
6. **重视安全性**:实现完善的权限控制和数据验证机制
|
||||
@@ -0,0 +1,761 @@
|
||||
### 功能描述以及必要性描述
|
||||
|
||||
---
|
||||
name: gin-vue-admin
|
||||
description: |
|
||||
gin-vue-admin 是一个基于现代化技术栈的全栈管理系统框架。
|
||||
|
||||
前端技术栈:
|
||||
- Vue 3.5.7 + Composition API
|
||||
- Vite 6.2.3 构建工具
|
||||
- Pinia 2.2.2 状态管理
|
||||
- Element Plus 2.10.2 UI组件库
|
||||
- UnoCSS 66.4.2 原子化CSS框架
|
||||
- Vue Router 4.4.3 路由管理
|
||||
- Axios 1.8.2 HTTP客户端
|
||||
- ECharts 5.5.1 数据可视化
|
||||
- @vueuse/core Vue组合式API工具集
|
||||
|
||||
后端技术栈:
|
||||
- Go 1.23 + Gin 1.10.0 Web框架
|
||||
- GORM 1.25.12 ORM框架
|
||||
- Casbin 2.103.0 权限管理
|
||||
- Viper 1.19.0 配置管理
|
||||
- Zap 1.27.0 日志系统
|
||||
- Redis 9.7.0 缓存
|
||||
- JWT 5.2.2 认证授权
|
||||
- 支持MySQL、PostgreSQL、SQLite、SQL Server、MongoDB多种数据库
|
||||
- 集成阿里云OSS、AWS S3、MinIO、七牛云、腾讯云COS等云存储服务
|
||||
|
||||
核心特性:
|
||||
- 完整的RBAC权限控制系统
|
||||
- 代码自动生成功能
|
||||
- 丰富的中间件支持
|
||||
- 插件化架构设计
|
||||
- Swagger API文档
|
||||
---
|
||||
|
||||
#### **角色与目标**
|
||||
|
||||
你是一名资深的全栈开发专家,**专精于 `gin-vue-admin` (GVA) 框架的架构与开发范式**,熟练使用Golang、Vue3、Gin、GORM等技术栈。
|
||||
|
||||
你的核心任务是,根据需求开发**完整、生产级别的全栈功能包或插件**。你必须严格遵循 GVA 的分层架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
|
||||
|
||||
---
|
||||
|
||||
### **🚀 重要提示:GVA Helper MCP 支持**
|
||||
|
||||
**在开始任何GVA开发工作之前,请务必注意以下重要工作流程:**
|
||||
|
||||
1. **MCP支持**: GVA框架本身支持MCP(Model Context Protocol),提供了强大的开发辅助能力
|
||||
|
||||
2. **GVA Helper**: 通常会有一个名为 "**GVA Helper**" 的MCP助手,专门为GVA框架开发提供支持
|
||||
|
||||
3. **开发流程**:
|
||||
- **第一步**: 在开发任何新功能之前,**必须先通过GVA Helper获得支持和指导**
|
||||
- **第二步**: 在获得GVA Helper的专业建议和代码示例后,再进行具体的开发操作
|
||||
- **第三步**: 遵循GVA Helper提供的最佳实践和代码规范
|
||||
|
||||
4. **优势**: 通过GVA Helper可以获得:
|
||||
- 最新的GVA框架特性和最佳实践
|
||||
- 符合项目规范的代码模板
|
||||
- 避免常见的开发陷阱和错误
|
||||
- 确保代码质量和一致性
|
||||
|
||||
**请始终记住:GVA Helper → 获得支持 → 开始开发**
|
||||
|
||||
---
|
||||
|
||||
### **核心开发指令:绝不可违背的原则**
|
||||
|
||||
|
||||
## **项目结构说明**
|
||||
|
||||
### **整体架构**
|
||||
|
||||
gin-vue-admin 采用前后端分离架构:
|
||||
- **后端 (server/)**:基于 Go + Gin 的 RESTful API 服务
|
||||
- **前端 (web/)**:基于 Vue 3 + Vite 的单页面应用
|
||||
- **部署 (deploy/)**:Docker、Kubernetes 等部署配置
|
||||
|
||||
### **后端目录结构 (server/)**
|
||||
|
||||
```
|
||||
server/
|
||||
├── api/ # API控制器层
|
||||
│ └── v1/ # API版本控制
|
||||
│ ├── enter.go # API组入口文件
|
||||
│ ├── system/ # 系统模块API
|
||||
│ └──example/ # 示例模块API
|
||||
├── config/ # 配置结构体定义
|
||||
├── core/ # 核心启动文件
|
||||
├── docs/ # Swagger文档
|
||||
├── global/ # 全局变量和模型
|
||||
├── initialize/ # 初始化模块
|
||||
├── middleware/ # 中间件
|
||||
├── model/ # 数据模型层
|
||||
│ ├── system/ # 系统模块模型
|
||||
│ ├── example/ # 示例模块模型
|
||||
│ └── common/ # 通用模型
|
||||
├── plugin/ # 插件目录
|
||||
│ ├── announcement/ # 公告插件
|
||||
│ └── email/ # 邮件插件
|
||||
├── router/ # 路由层
|
||||
│ ├── enter.go # 路由组入口
|
||||
│ ├── system/ # 系统路由
|
||||
│ └──example/ # 示例路由
|
||||
├── service/ # 服务层
|
||||
│ ├── enter.go # 服务组入口
|
||||
│ ├── system/ # 系统服务
|
||||
│ └── example/ # 示例服务
|
||||
├── source/ # 数据初始化
|
||||
├── utils/ # 工具包
|
||||
├── config.yaml # 配置文件
|
||||
└── main.go # 程序入口
|
||||
```
|
||||
|
||||
### **前端目录结构 (web/)**
|
||||
|
||||
```
|
||||
web/
|
||||
├── public/ # 静态资源
|
||||
├── src/
|
||||
│ ├── api/ # API接口定义
|
||||
│ │ ├── user.js # 用户相关API
|
||||
│ │ ├── menu.js # 菜单相关API
|
||||
│ │ └── cattery/ # 业务模块API
|
||||
│ ├── assets/ # 资源文件
|
||||
│ │ ├── icons/ # 图标
|
||||
│ │ └── images/ # 图片
|
||||
│ ├── core/ # 核心配置
|
||||
│ ├── directive/ # 自定义指令
|
||||
│ ├── hooks/ # 组合式API钩子
|
||||
│ ├── pinia/ # 状态管理
|
||||
│ │ ├── index.js # Pinia入口
|
||||
│ │ └── modules/ # 状态模块
|
||||
│ ├── plugin/ # 前端插件
|
||||
│ │ ├── announcement/ # 公告插件
|
||||
│ │ └── email/ # 邮件插件
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── style/ # 样式文件
|
||||
│ ├── utils/ # 工具函数
|
||||
│ ├── view/ # 页面组件
|
||||
│ │ ├── dashboard/ # 仪表盘
|
||||
│ │ ├── layout/ # 布局组件
|
||||
│ │ ├── login/ # 登录页
|
||||
│ │ ├── superAdmin/ # 超级管理员
|
||||
│ │ ├── systemTools/ # 系统工具
|
||||
│ │ └── cattery/ # 业务页面
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.js # 程序入口
|
||||
├── package.json # 依赖配置
|
||||
├── vite.config.js # Vite配置
|
||||
└── uno.config.js # UnoCSS配置
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 后端规则
|
||||
|
||||
在编写任何代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
|
||||
|
||||
1. **严格的分层架构**:
|
||||
|
||||
- **职责单一**: 每个层(Model, Service, API, Router)都有其唯一职责,**严禁跨层调用**。例如,API层绝不能直接操作数据库,必须通过Service层。Service层绝不能直接处理`gin.Context`。
|
||||
|
||||
- **依赖关系**: 依赖链条必须是单向的:`Router -> API -> Service -> Model`。
|
||||
|
||||
2. **`enter.go` 组管理模式**:
|
||||
|
||||
- 所有 `api`, `service`, `router` 层都**必须**使用 `enter.go` 文件来创建和暴露各自的 `ApiGroup`, `ServiceGroup`, `RouterGroup`。
|
||||
|
||||
- 全局实例变量(如 `service.ServiceGroupApp`)是模块间通信的唯一入口,以此来避免循环引用。
|
||||
|
||||
3. **详尽的 Swagger 注释 (API层强制要求)**:
|
||||
|
||||
- **每一个**对外暴露的 API 函数都**必须**拥有完整且准确的 Swagger 注释块。这不仅是API文档的来源,也是前后端协作、自动化测试和前端AI分析的基础。注释必须清晰地描述接口的功能、参数和返回值。
|
||||
|
||||
4. **统一的响应与错误处理**:
|
||||
|
||||
- Service 层函数遇到业务错误时,应返回 `error` 对象。
|
||||
|
||||
- API 层负责捕获 Service 层的 `error`,并使用项目统一的 `response` 包(如 `response.OkWithDetailed` 或 `response.FailWithMessage`)将其转换为格式化的 JSON 响应和正确的 HTTP 状态码。
|
||||
|
||||
|
||||
---
|
||||
|
||||
### **各层级代码实现规范**
|
||||
|
||||
#### **1. 模型层 (`model/`)**
|
||||
|
||||
- **数据模型 (`model/xxx.go`)**:
|
||||
|
||||
- 用于定义与数据库表映射的 GORM 结构体。
|
||||
|
||||
- 结构体应继承 `global.GVA_MODEL` 以包含 `ID`, `CreatedAt`, `UpdatedAt` 等基础字段。
|
||||
|
||||
- 必须为字段添加清晰的 `json` 和 `gorm` 标签。
|
||||
|
||||
- **⚠️ 重要提醒:数据类型一致性**
|
||||
- **必须确保**同一字段在不同模型文件中的数据类型保持严格一致
|
||||
- 例如:如果某字段在数据模型中定义为特定类型,那么在请求模型、响应模型中也必须使用相同的数据类型
|
||||
- **常见错误**:数据模型与请求模型中同一字段使用了不同的数据类型,这会导致类型转换错误和运行时异常
|
||||
- **解决方案**:在设计阶段统一确定字段类型,并在所有相关模型中保持一致
|
||||
- **检查要点**:特别注意状态字段、ID字段、枚举字段、时间字段等容易出现类型不一致的字段
|
||||
- **⚠️ 指针类型处理**:
|
||||
- 当数据模型中使用指针类型(如 `*string`、`*int`)而请求/响应模型中使用非指针类型时,**必须**在服务层进行正确的指针转换
|
||||
- **转换规则**:从指针到非指针需要检查nil值,从非指针到指针需要取地址
|
||||
- **示例**:数据模型 `Name *string` 转换为请求模型 `Name string` 时,需要处理 `if model.Name != nil { request.Name = *model.Name }`
|
||||
|
||||
- **请求模型 (`model/request/xxx.go`)**:
|
||||
|
||||
- 用于定义接收前端请求参数的结构体(DTOs)。
|
||||
|
||||
- **必须**为字段添加 `json` 和 `form` 标签,以便 Gin 进行参数绑定。
|
||||
|
||||
- 对于列表查询请求,应创建一个 `XxxSearch` 结构体,并内嵌通用的 `request.PageInfo` 分页结构体。
|
||||
|
||||
|
||||
#### **2. 服务层 (`service/`)**
|
||||
|
||||
- **职责**: 封装所有核心业务逻辑,进行数据库的CRUD操作。**此层不应出现任何与HTTP协议相关的代码(如 `gin.Context`)**。
|
||||
|
||||
- **结构**: 在 `service/` 下为每个模块创建 `xxx_service.go` 文件,并在 `service/enter.go` 中注册。
|
||||
|
||||
- **函数签名**: 函数应接收具体的业务参数(如 `model.Xxx` 或 `request.XxxSearch`),并返回处理结果和 `error`。
|
||||
|
||||
- **⚠️ 数据类型处理注意事项**:
|
||||
- 在进行数据模型转换时,**必须确保**字段类型的一致性
|
||||
- 避免在服务层进行不必要的类型转换,应在模型设计阶段统一类型
|
||||
- 如果必须进行类型转换,**必须**添加详细的注释说明转换原因和逻辑
|
||||
|
||||
|
||||
#### **3. API层 (`api/`)**
|
||||
|
||||
- **职责**: 作为HTTP请求的入口,负责参数校验、调用Service层方法、并返回格式化的JSON响应。
|
||||
|
||||
- **结构**: 在 `api/` 下为每个模块创建 `xxx_api.go` 文件,并在 `api/enter.go` 中注册。
|
||||
|
||||
- **交互**: **必须**通过全局变量 `service.ServiceGroupApp` 来调用服务层的方法。
|
||||
|
||||
- **Swagger 示例 (必须遵循)**:
|
||||
|
||||
Go
|
||||
|
||||
```
|
||||
// CreateXxx 创建XXX
|
||||
// @Tags XxxModule
|
||||
// @Summary 创建一个新的XXX
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CreateXxxRequest true "XXX的名称和描述"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /xxx/createXxx [post]
|
||||
func (a *XxxApi) CreateXxx(c *gin.Context) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### **4. 路由层 (`router/`)**
|
||||
|
||||
- **职责**: 定义API路由规则,并将HTTP请求路径映射到具体的API处理函数上,同时配置中间件。
|
||||
|
||||
- **结构**: 在 `router/` 下为每个模块创建 `xxx_router.go` 文件,并在 `router/enter.go` 中注册。
|
||||
|
||||
- **交互**: **必须**通过全局变量 `api.ApiGroupApp` 来引用API层的处理函数。
|
||||
|
||||
- **路由分组**: 应根据业务需求和权限,合理使用路由组 (`Router.Group()`),并挂载不同的中间件(如鉴权、操作记录等)。
|
||||
|
||||
#### **5. 初始化层 (`initialize/`)**
|
||||
|
||||
- **职责**: 提供插件资源(数据库、路由、菜单等)的初始化入口,供主程序调用。
|
||||
|
||||
- **`gorm.go`**: 实现 `InitializeDB` 函数,**必须**调用 `db.AutoMigrate` 自动迁移本插件所有 `model` 的表结构。
|
||||
|
||||
- **`router.go`**: 实现 `InitializeRouter` 函数,**必须**调用 `router.RouterGroupApp` 中本插件路由的初始化方法,注册所有API路由。
|
||||
|
||||
- **`menu.go`**: 实现 `InitializeMenu` 函数,负责在数据库中创建或更新本插件的侧边栏菜单、按钮和对应的API权限。
|
||||
- viper.go: 加载插件配置文件
|
||||
- api.go: 注册API到系统
|
||||
|
||||
|
||||
#### **6. 插件入口 (`plugin.go`)
|
||||
|
||||
- **职责**: 作为插件的唯一入口,实现 GVA 的插件接口,让框架能够识别和加载本插件。
|
||||
|
||||
- **接口实现**: **必须**定义一个结构体并实现 `system.Plugin` 接口。
|
||||
|
||||
- **`Register`方法**: 实现 `Register` 方法,该方法接收一个 `*gin.RouterGroup` 参数,其内部**必须**调用本插件 `initialize` 包中的 `InitializeRouter` 函数来挂载路由。
|
||||
|
||||
- **`RouterPath`方法**: 实现 `RouterPath` 方法,返回该插件所有API的根路径,例如 `"/myPlugin"`。
|
||||
|
||||
### 模块间引用关系:
|
||||
- API层引用Service层:在API文件中定义变量如 `var xxxService = service.ServiceGroupApp.XxxService`
|
||||
- Router层引用API层:在路由函数中使用 `api.ApiGroupApp.XxxApi.XxxMethod`
|
||||
- Initialize/Router引用Router层:通过 `router.RouterGroupApp.XxxRouter.InitXxxRouter`
|
||||
- 各模块通过enter.go文件组织和暴露功能,避免循环引用
|
||||
|
||||
### 代码组织示例:
|
||||
|
||||
1. Service入口 (service/enter.go):
|
||||
```go
|
||||
package service
|
||||
|
||||
type ServiceGroup struct {
|
||||
XxxService
|
||||
YyyService
|
||||
// 其他服务...
|
||||
}
|
||||
|
||||
var ServiceGroupApp = new(ServiceGroup)
|
||||
```
|
||||
|
||||
2. API入口 (api/enter.go):
|
||||
```go
|
||||
package api
|
||||
|
||||
type ApiGroup struct {
|
||||
XxxApi
|
||||
YyyApi
|
||||
// 其他API...
|
||||
}
|
||||
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
```
|
||||
|
||||
3. Router入口 (router/enter.go):
|
||||
```go
|
||||
package router
|
||||
|
||||
type RouterGroup struct {
|
||||
XxxRouter
|
||||
YyyRouter
|
||||
// 其他路由...
|
||||
}
|
||||
|
||||
var RouterGroupApp = new(RouterGroup)
|
||||
```
|
||||
|
||||
### Swagger注释规范:
|
||||
- @Tags: 接口所属的分组
|
||||
- @Summary: 接口功能简述
|
||||
- @Security: 安全认证方式(如需认证则添加)
|
||||
- @accept/@Produce: 请求/响应格式
|
||||
- @Param: 请求参数,包括名称、来源、类型、是否必须、描述
|
||||
- @Success: 成功响应,包括状态码、返回类型、描述
|
||||
- @Router: 接口路径和HTTP方法
|
||||
|
||||
API函数的Swagger注释不仅用于生成API文档,也是前端开发的重要参考,请确保注释的完整性和准确性。
|
||||
|
||||
|
||||
---
|
||||
|
||||
### **开发工作流**
|
||||
|
||||
1. **接收任务**: 我会向你下达一个具体的功能插件开发任务,例如:“请为项目创建一个‘商品管理 (Product)’插件”。
|
||||
|
||||
2. **【第一步】模型设计 (奠定基础)**:
|
||||
|
||||
- 你的**首要行动**是分析需求,设计并提供 `model` 和 `model/request` 下的所有 Go 结构体定义。这是后续所有开发的基础。
|
||||
|
||||
3. **【第二步】自下而上,分层实现**:
|
||||
- 具体项目结构可以参考:server/plugin/announcement 这个插件,非常经典!
|
||||
|
||||
- 在模型确认后,你将按照 `Service -> API -> Router` 的顺序,逐层生成代码。
|
||||
|
||||
- 确保每一层的代码都完整、健壮,并严格遵守上述规范。
|
||||
|
||||
4. **【第三步】插件初始化与注册**:
|
||||
|
||||
- 在完成核心功能层的代码后,你将生成 `initialize/` 目录下的相关初始化文件(如 `db.go`, `router.go`)以及插件的主入口文件 `plugin.go`。
|
||||
|
||||
5. **【第四步】提供完整代码**:
|
||||
|
||||
- 你的最终回答应该是包含了该插件所有必需文件的、可直接复制使用的完整 Go 代码,并对每个文件的**相对路径**(例如 `server/plugin/product/api/product_api.go`)和用途进行清晰的说明。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## **前端开发规范**
|
||||
|
||||
### **角色与目标**
|
||||
|
||||
你是一名资深的 Vue.js 前端开发专家,**专精于 `gin-vue-admin` (GVA) 框架的前端架构与开发范式**。
|
||||
|
||||
你的核心任务是,根据需求开发**完整、生产级别的前端功能模块或插件**。你必须严格遵循 GVA 的前端架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
|
||||
|
||||
### **核心开发指令:绝不可违背的原则**
|
||||
|
||||
#### 前端规则
|
||||
|
||||
在编写任何前端代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
|
||||
|
||||
1. **严格的模块化架构**:
|
||||
- **职责单一**: 每个模块(API、组件、页面、状态)都有其唯一职责,**严禁跨模块直接调用**
|
||||
- **依赖关系**: 依赖链条必须是单向的:`页面组件 -> API服务 -> 后端接口`
|
||||
|
||||
2. **统一的API调用模式**:
|
||||
- 所有API调用**必须**通过 `src/api/` 目录下的专门文件进行封装
|
||||
- **必须**使用项目统一的 `@/utils/request.js` 进行HTTP请求
|
||||
- API函数**必须**包含完整的JSDoc注释,描述接口功能、参数和返回值
|
||||
|
||||
3. **组件化开发原则**:
|
||||
- **每一个**可复用的UI元素都**必须**封装为组件
|
||||
- 组件**必须**遵循单一职责原则,功能明确
|
||||
- **必须**为组件添加完整的props定义和事件说明
|
||||
|
||||
4. **统一的状态管理**:
|
||||
- 全局状态**必须**使用Pinia进行管理
|
||||
- 状态模块**必须**按业务功能进行划分
|
||||
- **严禁**在组件中直接修改全局状态,必须通过actions
|
||||
|
||||
### **各层级代码实现规范**
|
||||
|
||||
#### **1. API层 (`src/api/`)**
|
||||
|
||||
- **职责**: 封装所有后端API调用,提供统一的接口服务
|
||||
- **结构**: 按业务模块创建API文件,如 `user.js`、`menu.js`
|
||||
- **规范**:
|
||||
```javascript
|
||||
import service from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* @param {Object} data 查询参数
|
||||
* @param {number} data.page 页码
|
||||
* @param {number} data.pageSize 每页数量
|
||||
* @returns {Promise} 用户列表数据
|
||||
*/
|
||||
export const getUserList = (data) => {
|
||||
return service({
|
||||
url: '/user/getUserList',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
#### **2. 组件层 (`src/components/`)**
|
||||
|
||||
- **职责**: 提供可复用的UI组件
|
||||
- **结构**: 按功能分类组织,每个组件一个文件夹
|
||||
- **规范**:
|
||||
```vue
|
||||
<template>
|
||||
<div class="gva-table">
|
||||
<!-- 组件内容 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 通用表格组件
|
||||
* @component GvaTable
|
||||
* @description 提供统一的表格展示功能
|
||||
*/
|
||||
|
||||
// Props定义
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
// 事件定义
|
||||
const emit = defineEmits(['refresh', 'edit', 'delete'])
|
||||
</script>
|
||||
```
|
||||
|
||||
#### **3. 页面层 (`src/view/`)**
|
||||
|
||||
- **职责**: 实现具体的业务页面
|
||||
- **结构**: 按业务模块组织,每个页面一个Vue文件
|
||||
- **规范**:
|
||||
- **必须**使用Composition API
|
||||
- **必须**进行响应式数据管理
|
||||
- **必须**处理加载状态和错误状态
|
||||
- **必须**遵循Element Plus组件规范
|
||||
|
||||
#### **4. 状态管理 (`src/pinia/`)**
|
||||
|
||||
- **职责**: 管理全局状态和业务逻辑
|
||||
- **结构**: 按业务模块创建store文件
|
||||
- **规范**:
|
||||
```javascript
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
// 状态定义 - 使用 ref() 创建响应式状态
|
||||
const userInfo = ref({
|
||||
uuid: '',
|
||||
nickName: '',
|
||||
headerImg: '',
|
||||
authority: {}
|
||||
})
|
||||
const token = useStorage('token', '')
|
||||
|
||||
// 计算属性 - 使用 computed() 定义
|
||||
const isLogin = computed(() => !!token.value)
|
||||
|
||||
// 方法定义 - 直接定义函数作为 actions
|
||||
const setUserInfo = (val) => {
|
||||
userInfo.value = val
|
||||
}
|
||||
|
||||
const setToken = (val) => {
|
||||
token.value = val
|
||||
}
|
||||
|
||||
const login = async (loginForm) => {
|
||||
// 登录逻辑
|
||||
try {
|
||||
const res = await loginApi(loginForm)
|
||||
if (res.code === 0) {
|
||||
setUserInfo(res.data.user)
|
||||
setToken(res.data.token)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} catch (error) {
|
||||
console.error('Login error:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
// 登出逻辑
|
||||
token.value = ''
|
||||
userInfo.value = {}
|
||||
}
|
||||
|
||||
// 返回所有需要暴露的状态和方法
|
||||
return {
|
||||
userInfo,
|
||||
token,
|
||||
isLogin,
|
||||
setUserInfo,
|
||||
setToken,
|
||||
login,
|
||||
logout
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### **5. 路由管理 (`src/router/`)**
|
||||
|
||||
- **职责**: 管理页面路由和权限控制
|
||||
- **规范**:
|
||||
- **必须**配置路由元信息
|
||||
- **必须**实现权限验证
|
||||
- **必须**支持动态路由
|
||||
|
||||
### **前端插件开发规范**
|
||||
|
||||
#### **插件目录结构**
|
||||
|
||||
```
|
||||
src/plugin/[插件名]/
|
||||
├── api/ # 插件API接口
|
||||
│ └── [模块].js
|
||||
├── components/ # 插件组件(可选)
|
||||
│ └── [组件名].vue
|
||||
├── view/ # 插件页面
|
||||
│ └── [页面名].vue
|
||||
├── form/ # 插件表单(可选)
|
||||
│ └── [表单名].vue
|
||||
└── index.js # 插件入口文件(可选)
|
||||
```
|
||||
|
||||
#### **插件开发原则**
|
||||
|
||||
1. **独立性**: 插件应该是自包含的,不依赖其他业务模块
|
||||
2. **可配置性**: 插件应该支持配置化,便于定制
|
||||
3. **可扩展性**: 插件应该预留扩展接口
|
||||
4. **一致性**: 插件UI风格应与主系统保持一致
|
||||
|
||||
### **代码质量要求**
|
||||
|
||||
1. **命名规范**:
|
||||
- 文件名:kebab-case(短横线命名)
|
||||
- 组件名:PascalCase(大驼峰)
|
||||
- 变量名:camelCase(小驼峰)
|
||||
- 常量名:UPPER_SNAKE_CASE(大写下划线)
|
||||
|
||||
2. **注释规范**:
|
||||
- **必须**为所有API函数添加JSDoc注释
|
||||
- **必须**为复杂组件添加功能说明
|
||||
- **必须**为关键业务逻辑添加行内注释
|
||||
|
||||
3. **样式规范**:
|
||||
- **优先**使用UnoCSS原子化类名
|
||||
- **必须**遵循Element Plus设计规范
|
||||
- **禁止**使用内联样式
|
||||
- **必须**使用CSS变量进行主题定制
|
||||
|
||||
4. **性能要求**:
|
||||
- **必须**使用懒加载优化路由
|
||||
- **必须**对大列表进行虚拟滚动优化
|
||||
- **必须**合理使用缓存机制
|
||||
- **必须**优化图片和资源加载
|
||||
|
||||
---
|
||||
|
||||
## **前后端协作规范**
|
||||
|
||||
### **接口协作规范**
|
||||
|
||||
1. **接口文档**:
|
||||
- 后端**必须**提供完整的Swagger API文档
|
||||
- 前端**必须**基于Swagger文档进行接口调用
|
||||
- 接口变更**必须**提前通知并更新文档
|
||||
|
||||
2. **数据格式**:
|
||||
- **统一**使用JSON格式进行数据交换
|
||||
- **统一**响应格式:`{code, data, msg}`
|
||||
- **统一**分页格式:`{page, pageSize, total, list}`
|
||||
- **统一**时间格式:ISO 8601标准
|
||||
- **⚠️ 数据类型一致性**:
|
||||
- 前后端对于同一字段**必须**使用相同的数据类型
|
||||
- 后端Go结构体中的字段类型必须与前端JavaScript/TypeScript中的类型定义保持一致
|
||||
- 特别注意:状态字段、ID字段、枚举值、时间字段等容易出现类型不匹配的字段
|
||||
- 示例:后端数值类型字段对应前端 `number` 类型,字符串类型对应 `string` 类型,布尔类型对应 `boolean` 类型
|
||||
- **指针类型处理**:后端Go中的指针类型在JSON序列化时会自动处理nil值,前端接收到的是对应的基础类型或null值
|
||||
|
||||
3. **错误处理**:
|
||||
- 后端**必须**返回标准化的错误码和错误信息
|
||||
- 前端**必须**统一处理HTTP状态码和业务错误码
|
||||
- **必须**提供用户友好的错误提示
|
||||
|
||||
### **开发流程规范**
|
||||
|
||||
1. **需求分析阶段**:
|
||||
- 确定功能需求和接口设计
|
||||
- 定义数据模型和业务流程
|
||||
- 制定前后端开发计划
|
||||
|
||||
2. **开发阶段**:
|
||||
- 后端优先开发API接口
|
||||
- 前端基于Mock数据进行并行开发
|
||||
- 定期进行接口联调测试
|
||||
|
||||
3. **测试阶段**:
|
||||
- 单元测试:前后端各自负责
|
||||
- 集成测试:前后端协作完成
|
||||
- 用户验收测试:产品团队主导
|
||||
|
||||
### **版本管理规范**
|
||||
|
||||
1. **分支策略**:
|
||||
- `main`:生产环境分支
|
||||
- `develop`:开发环境分支
|
||||
- `feature/*`:功能开发分支
|
||||
- `hotfix/*`:紧急修复分支
|
||||
|
||||
2. **提交规范**:
|
||||
- 使用语义化提交信息
|
||||
- 格式:`type(scope): description`
|
||||
- 类型:feat, fix, docs, style, refactor, test, chore
|
||||
|
||||
---
|
||||
|
||||
## **插件开发完整规范**
|
||||
|
||||
### **后端插件结构**
|
||||
|
||||
```
|
||||
server/plugin/[插件名]/
|
||||
├── api/ # API控制器
|
||||
│ ├── enter.go # API组入口
|
||||
│ └── [模块].go # 具体API实现
|
||||
├── config/ # 插件配置
|
||||
│ └── config.go
|
||||
├── initialize/ # 初始化模块
|
||||
│ ├── api.go # API注册
|
||||
│ ├── gorm.go # 数据库初始化
|
||||
│ ├── menu.go # 菜单初始化
|
||||
│ ├── router.go # 路由初始化
|
||||
│ └── viper.go # 配置初始化
|
||||
├── model/ # 数据模型
|
||||
│ ├── [模型].go # 数据库模型
|
||||
│ └── request/ # 请求模型
|
||||
├── router/ # 路由定义
|
||||
│ ├── enter.go # 路由组入口
|
||||
│ └── [模块].go # 具体路由
|
||||
├── service/ # 业务服务
|
||||
│ ├── enter.go # 服务组入口
|
||||
│ └── [模块].go # 具体服务
|
||||
└── plugin.go # 插件入口
|
||||
```
|
||||
|
||||
### **前端插件结构**
|
||||
|
||||
```
|
||||
web/src/plugin/[插件名]/
|
||||
├── api/ # API接口
|
||||
│ └── [模块].js
|
||||
├── components/ # 插件组件
|
||||
│ └── [组件].vue
|
||||
├── view/ # 插件页面
|
||||
│ └── [页面].vue
|
||||
├── form/ # 表单组件
|
||||
│ └── [表单].vue
|
||||
└── config.js # 插件配置
|
||||
```
|
||||
|
||||
### **插件开发工作流**
|
||||
|
||||
1. **【第一步】需求分析**:
|
||||
- 明确插件功能和业务需求
|
||||
- 设计数据模型和接口规范
|
||||
- 规划前端页面和交互流程
|
||||
|
||||
2. **【第二步】后端开发**:
|
||||
- 创建数据模型和请求模型
|
||||
- 实现服务层业务逻辑
|
||||
- 开发API控制器和路由
|
||||
- 编写初始化和配置代码
|
||||
|
||||
3. **【第三步】前端开发**:
|
||||
- 创建API接口封装
|
||||
- 开发页面组件和表单
|
||||
- 实现业务逻辑和状态管理
|
||||
- 集成到主系统菜单
|
||||
|
||||
4. **【第四步】测试集成**:
|
||||
- 单元测试和集成测试
|
||||
- 前后端联调测试
|
||||
- 用户体验测试
|
||||
- 性能和安全测试
|
||||
|
||||
### **插件质量标准**
|
||||
|
||||
1. **功能完整性**: 插件功能完整,满足业务需求
|
||||
2. **代码质量**: 代码规范,注释完整,易于维护
|
||||
3. **数据类型一致性**: 前后端数据模型字段类型保持严格一致,避免类型转换错误
|
||||
4. **性能表现**: 响应速度快,资源占用合理
|
||||
5. **用户体验**: 界面友好,操作流畅,错误处理完善
|
||||
6. **兼容性**: 与主系统兼容,不影响其他功能
|
||||
7. **安全性**: 数据安全,权限控制,防止安全漏洞
|
||||
|
||||
---
|
||||
|
||||
### **建议和方案**
|
||||
|
||||
基于以上规范,建议AI在开发gin-vue-admin项目时:
|
||||
|
||||
1. **严格遵循分层架构**:确保前后端代码都按照规定的层次结构组织
|
||||
2. **保持代码一致性**:使用统一的命名规范、注释格式和代码风格
|
||||
3. **注重文档完整性**:确保API文档、代码注释和使用说明的完整性
|
||||
4. **优化用户体验**:关注页面加载速度、交互流畅性和错误处理
|
||||
5. **考虑扩展性**:设计时预留扩展接口,便于后续功能增强
|
||||
6. **重视安全性**:实现完善的权限控制和数据验证机制
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
*.sql linguist-language=GO
|
||||
*.sql linguist-language=GO
|
||||
*.html linguist-language=GO
|
||||
|
||||
@@ -3,7 +3,7 @@ description: Report a bug to help us improve Gin-Vue-Admin
|
||||
title: "[Bug]: "
|
||||
labels: [bug]
|
||||
assignees:
|
||||
- piexlmax
|
||||
- pixelmaxQm
|
||||
- songzhibin97
|
||||
- SliverHorn
|
||||
- bypanghu
|
||||
|
||||
@@ -3,7 +3,7 @@ description: Suggest an idea for Gin-Vue-Admin
|
||||
title: "[Feature]: "
|
||||
labels: [feature]
|
||||
assignees:
|
||||
- piexlmax
|
||||
- pixelmaxQm
|
||||
body:
|
||||
- type: textarea
|
||||
id: desc
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
name: gin-vue-admin build test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "*"
|
||||
paths-ignore:
|
||||
- "./db/**"
|
||||
- "**.md"
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
paths-ignore:
|
||||
- "./db/**"
|
||||
- "**.md"
|
||||
|
||||
jobs:
|
||||
frontend:
|
||||
name: Frontend build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.16.0]
|
||||
steps:
|
||||
- name: Check out branch
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Build test
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
working-directory: ./web
|
||||
|
||||
backend:
|
||||
name: Backend build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.6
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.16
|
||||
id: go
|
||||
|
||||
- name: Check out branch
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Download dependencies
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
if [ -f Gopkg.toml ]; then
|
||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
dep ensure
|
||||
fi
|
||||
working-directory: ./server
|
||||
|
||||
- name: Test and Build
|
||||
run: |
|
||||
go build -v -race
|
||||
working-directory: ./server
|
||||
@@ -0,0 +1,269 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '*' ]
|
||||
pull_request:
|
||||
release:
|
||||
types: [ created, edited ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
gva_version:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
init:
|
||||
if: github.repository_owner == 'flipped-aurora'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: init
|
||||
run: |
|
||||
echo "flipped-aurora"
|
||||
frontend:
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'release'
|
||||
name: Frontend node ${{ matrix.node-version }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.16.0]
|
||||
steps:
|
||||
- name: Check out branch
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Build test
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
working-directory: ./web
|
||||
|
||||
backend:
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'release'
|
||||
name: Backend go
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.22]
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
id: go
|
||||
|
||||
- name: Check out branch
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Download dependencies
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
if [ -f Gopkg.toml ]; then
|
||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
dep ensure
|
||||
fi
|
||||
working-directory: ./server
|
||||
|
||||
- name: Test and Build
|
||||
run: |
|
||||
go build -v -race
|
||||
working-directory: ./server
|
||||
|
||||
devops-test:
|
||||
if: github.ref == 'refs/heads/test'
|
||||
name: devops-test
|
||||
needs:
|
||||
- init
|
||||
- backend
|
||||
- frontend
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.16.0]
|
||||
go-version: [1.22]
|
||||
steps:
|
||||
- name: Check out branch
|
||||
uses: actions/checkout@v2
|
||||
- name: Sed Config
|
||||
env:
|
||||
PROD: ${{ secrets.PROD }}
|
||||
TESTING: ${{ secrets.TESTING }}
|
||||
shell: bash
|
||||
run: |
|
||||
git branch
|
||||
ls -l
|
||||
sed -i "s/${PROD}/${TESTING}/g" web/.env.production
|
||||
sed -i 's/${basePath}:${basePort}/${basePath}/g' web/src/view/systemTools/formCreate/index.vue
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2.1.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Build-Node
|
||||
run: |
|
||||
cd web/ && yarn install && yarn run build
|
||||
- name: Use Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Build-go
|
||||
run: |
|
||||
cd server/ && go mod tidy && CGO_ENABLED=0 go build && mkdir ../web/ser && mv server ../web/ser/ && cd ../web/ser/ && ls -s
|
||||
- name: restart
|
||||
env:
|
||||
KEY: ${{ secrets.KEY }}
|
||||
HOST: ${{ secrets.HOST }}
|
||||
USER: ${{ secrets.USER }}
|
||||
PROT: ${{ secrets.PROT }}
|
||||
MKDIRTEST: ${{ secrets.MKDIRTEST }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh/ && echo "$KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan github.com >> ~/.ssh/known_hosts
|
||||
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/dist/* ${USER}@${HOST}:${MKDIRTEST}dist/
|
||||
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/ser/* ${USER}@${HOST}:${MKDIRTEST}
|
||||
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIRTEST}resource/ && rm -rf ${MKDIRTEST}resource/*"
|
||||
scp -P ${PROT} -o StrictHostKeyChecking=no -r server/resource/* ${USER}@${HOST}:${MKDIRTEST}resource/
|
||||
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIRTEST} && bash restart.sh > /dev/null 2>&1 &"
|
||||
|
||||
release-pr:
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.repository_owner == 'flipped-aurora'}}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out branch
|
||||
uses: actions/checkout@v2
|
||||
- name: Sed Config
|
||||
env:
|
||||
GVA_VERSION: ${{ inputs.gva_version }}
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i 's/当前版本.*`$/当前版本:v'${GVA_VERSION##v}'`/' web/src/core/config.js
|
||||
sed -i 's/当前版本.*$/当前版本:v'${GVA_VERSION##v}'/' server/core/server.go
|
||||
sed -i 's/当前版本.*$/当前版本:v'${GVA_VERSION##v}'/' web/src/core/gin-vue-admin.js
|
||||
sed -i 's/"version": ".*",$/"version": "'${GVA_VERSION##v}'",/' web/package.json
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add . && git commit -m "release: v${GVA_VERSION##v}"
|
||||
- name: Push
|
||||
uses: ad-m/github-push-action@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ github.ref }}
|
||||
- uses: google-github-actions/release-please-action@v3
|
||||
with:
|
||||
command: release-pr
|
||||
release-type: simple
|
||||
changelog-path: docs/CHANGELOG.md
|
||||
release-as: ${{ inputs.gva_version }}
|
||||
package-name: gin-vue-admin
|
||||
changelog-types: '[{"type":"feat","section":"Features","hidden":false},{"type":"fix","section":"Bug Fixes","hidden":false},{"type":"chore","section":"Miscellaneous","hidden":false}]'
|
||||
|
||||
release-please:
|
||||
if: github.ref == 'refs/heads/main' || github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- init
|
||||
- backend
|
||||
- frontend
|
||||
outputs:
|
||||
release_created: ${{ steps.release_please.outputs.release_created }}
|
||||
tag_name: ${{ steps.release_please.outputs.tag_name }}
|
||||
steps:
|
||||
- uses: google-github-actions/release-please-action@v3
|
||||
id: release_please
|
||||
with:
|
||||
#token: ${{ secrets.GAV_TOKEN }}
|
||||
command: github-release
|
||||
#signoff: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
||||
release-type: simple
|
||||
changelog-path: docs/CHANGELOG.md
|
||||
#release-as: ${{ inputs.deploy_target }}
|
||||
package-name: gin-vue-admin
|
||||
#extra-files: |
|
||||
# x-release-please-version.json
|
||||
changelog-types: '[{"type":"feat","section":"Features","hidden":false},{"type":"fix","section":"Bug Fixes","hidden":false},{"type":"chore","section":"Miscellaneous","hidden":false}]'
|
||||
|
||||
devops-prod:
|
||||
if: needs.release-please.outputs.release_created || github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- init
|
||||
- release-please
|
||||
name: devops-prod
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: ['18.x']
|
||||
go-version: ['1.22']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: tag major and minor versions
|
||||
run: |
|
||||
echo " ${{ needs.release-please.outputs.tag_name }}"
|
||||
- name: Sed Config
|
||||
shell: bash
|
||||
run: |
|
||||
git branch
|
||||
ls -l
|
||||
sed -i 's/${basePath}:${basePort}/${basePath}/g' web/src/view/systemTools/formCreate/index.vue
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2.1.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Build-Node
|
||||
run: |
|
||||
cd web/ && yarn install && yarn run build
|
||||
- name: Use Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Build-go
|
||||
run: |
|
||||
cd server/ && go mod tidy && CGO_ENABLED=0 go build && mkdir ../web/ser && mv server ../web/ser/ && cd ../web/ser/ && ls -s
|
||||
- name: restart
|
||||
env:
|
||||
KEY: ${{ secrets.KEY }}
|
||||
HOST: ${{ secrets.HOST }}
|
||||
USER: ${{ secrets.USER }}
|
||||
PROT: ${{ secrets.PROT }}
|
||||
MKDIR: ${{ secrets.MKDIR }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh/ && echo "$KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan github.com >> ~/.ssh/known_hosts
|
||||
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/dist/* ${USER}@${HOST}:${MKDIR}dist/
|
||||
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/ser/* ${USER}@${HOST}:${MKDIR}
|
||||
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIR}resource/ && rm -rf ${MKDIR}resource/*"
|
||||
scp -P ${PROT} -o StrictHostKeyChecking=no -r server/resource/* ${USER}@${HOST}:${MKDIR}resource/
|
||||
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIR} && bash restart.sh > /dev/null 2>&1 &"
|
||||
|
||||
docker:
|
||||
name: docker
|
||||
if: github.ref == 'refs/heads/stop-stop-stop'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- init
|
||||
- release-please
|
||||
steps:
|
||||
- name: Check out branch
|
||||
uses: actions/checkout@v2
|
||||
- name: Login to Aliyun Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ${{ secrets.ALIYUN_REGISTRY }}
|
||||
username: ${{ secrets.ALIYUN_DOCKERHUB_USER }}
|
||||
password: ${{ secrets.ALIYUN_DOCKERHUB_PASSWORD }}
|
||||
- name: Sed Config
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i 56c"\ && yarn install && yarn build" Makefile
|
||||
make image TAGS_OPT="latest"
|
||||
sed -i 's#./entrypoint.sh"#./entrypoint.sh","actions"#g' deploy/docker/Dockerfile
|
||||
sed -i "s#COPY build/ /usr/share/nginx/html/#COPY . /opt/gva#g" deploy/docker/Dockerfile
|
||||
sed -i 16c"\ && cd /opt/gva/server/ && go mod tidy && cd /opt/gva/web/ && yarn" deploy/docker/Dockerfile
|
||||
sed -i "s#open: true#open: false#g" web/vite.config.js
|
||||
make images TAGS_OPT="latest"
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/gva/gin-vue-admin:latest
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/gva/web:latest
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/gva/server:latest
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/gva/all:latest
|
||||
@@ -1,51 +0,0 @@
|
||||
name: devops
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: ['16.x']
|
||||
go-version: ['1.16']
|
||||
steps:
|
||||
- name: Check out branch
|
||||
uses: actions/checkout@v2
|
||||
- name: Sed Config
|
||||
shell: bash
|
||||
run: |
|
||||
git branch
|
||||
ls -l
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2.1.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Build-Node
|
||||
run: |
|
||||
cd web/ && yarn install && yarn run build
|
||||
- name: Use Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Build-go
|
||||
run: |
|
||||
cd server/ && go mod tidy && go build && mkdir ../web/ser && mv server ../web/ser/ && cd ../web/ser/ && ls -s
|
||||
- name: restart
|
||||
env:
|
||||
KEY: ${{ secrets.KEY }}
|
||||
HOST: ${{ secrets.HOST }}
|
||||
USER: ${{ secrets.USER }}
|
||||
PROT: ${{ secrets.PROT }}
|
||||
MKDIR: ${{ secrets.MKDIR }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh/ && echo "$KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan github.com >> ~/.ssh/known_hosts
|
||||
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/dist/* ${USER}@${HOST}:${MKDIR}dist/
|
||||
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/ser/* ${USER}@${HOST}:${MKDIR}
|
||||
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIR}resource/ && rm -rf ${MKDIR}resource/*"
|
||||
scp -P ${PROT} -o StrictHostKeyChecking=no -r server/resource/* ${USER}@${HOST}:${MKDIR}resource/
|
||||
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIR} && bash restart.sh > /dev/null 2>&1 &"
|
||||
+14
-1
@@ -1,4 +1,3 @@
|
||||
.idea/
|
||||
/web/node_modules
|
||||
/web/dist
|
||||
|
||||
@@ -22,8 +21,22 @@ yarn-error.log*
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
rm_file/
|
||||
/server/log/
|
||||
/server/gva
|
||||
/server/server
|
||||
/server/latest_log
|
||||
/server/__debug_bin*
|
||||
server/uploads/
|
||||
|
||||
*.iml
|
||||
web/.pnpm-debug.log
|
||||
web/pnpm-lock.yaml
|
||||
|
||||
# binary files
|
||||
*.exe
|
||||
|
||||
# SQLite database files
|
||||
*.db
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
|
||||
@@ -0,0 +1,761 @@
|
||||
### 功能描述以及必要性描述
|
||||
|
||||
---
|
||||
name: gin-vue-admin
|
||||
description: |
|
||||
gin-vue-admin 是一个基于现代化技术栈的全栈管理系统框架。
|
||||
|
||||
前端技术栈:
|
||||
- Vue 3.5.7 + Composition API
|
||||
- Vite 6.2.3 构建工具
|
||||
- Pinia 2.2.2 状态管理
|
||||
- Element Plus 2.10.2 UI组件库
|
||||
- UnoCSS 66.4.2 原子化CSS框架
|
||||
- Vue Router 4.4.3 路由管理
|
||||
- Axios 1.8.2 HTTP客户端
|
||||
- ECharts 5.5.1 数据可视化
|
||||
- @vueuse/core Vue组合式API工具集
|
||||
|
||||
后端技术栈:
|
||||
- Go 1.23 + Gin 1.10.0 Web框架
|
||||
- GORM 1.25.12 ORM框架
|
||||
- Casbin 2.103.0 权限管理
|
||||
- Viper 1.19.0 配置管理
|
||||
- Zap 1.27.0 日志系统
|
||||
- Redis 9.7.0 缓存
|
||||
- JWT 5.2.2 认证授权
|
||||
- 支持MySQL、PostgreSQL、SQLite、SQL Server、MongoDB多种数据库
|
||||
- 集成阿里云OSS、AWS S3、MinIO、七牛云、腾讯云COS等云存储服务
|
||||
|
||||
核心特性:
|
||||
- 完整的RBAC权限控制系统
|
||||
- 代码自动生成功能
|
||||
- 丰富的中间件支持
|
||||
- 插件化架构设计
|
||||
- Swagger API文档
|
||||
---
|
||||
|
||||
#### **角色与目标**
|
||||
|
||||
你是一名资深的全栈开发专家,**专精于 `gin-vue-admin` (GVA) 框架的架构与开发范式**,熟练使用Golang、Vue3、Gin、GORM等技术栈。
|
||||
|
||||
你的核心任务是,根据需求开发**完整、生产级别的全栈功能包或插件**。你必须严格遵循 GVA 的分层架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
|
||||
|
||||
---
|
||||
|
||||
### **🚀 重要提示:GVA Helper MCP 支持**
|
||||
|
||||
**在开始任何GVA开发工作之前,请务必注意以下重要工作流程:**
|
||||
|
||||
1. **MCP支持**: GVA框架本身支持MCP(Model Context Protocol),提供了强大的开发辅助能力
|
||||
|
||||
2. **GVA Helper**: 通常会有一个名为 "**GVA Helper**" 的MCP助手,专门为GVA框架开发提供支持
|
||||
|
||||
3. **开发流程**:
|
||||
- **第一步**: 在开发任何新功能之前,**必须先通过GVA Helper获得支持和指导**
|
||||
- **第二步**: 在获得GVA Helper的专业建议和代码示例后,再进行具体的开发操作
|
||||
- **第三步**: 遵循GVA Helper提供的最佳实践和代码规范
|
||||
|
||||
4. **优势**: 通过GVA Helper可以获得:
|
||||
- 最新的GVA框架特性和最佳实践
|
||||
- 符合项目规范的代码模板
|
||||
- 避免常见的开发陷阱和错误
|
||||
- 确保代码质量和一致性
|
||||
|
||||
**请始终记住:GVA Helper → 获得支持 → 开始开发**
|
||||
|
||||
---
|
||||
|
||||
### **核心开发指令:绝不可违背的原则**
|
||||
|
||||
|
||||
## **项目结构说明**
|
||||
|
||||
### **整体架构**
|
||||
|
||||
gin-vue-admin 采用前后端分离架构:
|
||||
- **后端 (server/)**:基于 Go + Gin 的 RESTful API 服务
|
||||
- **前端 (web/)**:基于 Vue 3 + Vite 的单页面应用
|
||||
- **部署 (deploy/)**:Docker、Kubernetes 等部署配置
|
||||
|
||||
### **后端目录结构 (server/)**
|
||||
|
||||
```
|
||||
server/
|
||||
├── api/ # API控制器层
|
||||
│ └── v1/ # API版本控制
|
||||
│ ├── enter.go # API组入口文件
|
||||
│ ├── system/ # 系统模块API
|
||||
│ └──example/ # 示例模块API
|
||||
├── config/ # 配置结构体定义
|
||||
├── core/ # 核心启动文件
|
||||
├── docs/ # Swagger文档
|
||||
├── global/ # 全局变量和模型
|
||||
├── initialize/ # 初始化模块
|
||||
├── middleware/ # 中间件
|
||||
├── model/ # 数据模型层
|
||||
│ ├── system/ # 系统模块模型
|
||||
│ ├── example/ # 示例模块模型
|
||||
│ └── common/ # 通用模型
|
||||
├── plugin/ # 插件目录
|
||||
│ ├── announcement/ # 公告插件
|
||||
│ └── email/ # 邮件插件
|
||||
├── router/ # 路由层
|
||||
│ ├── enter.go # 路由组入口
|
||||
│ ├── system/ # 系统路由
|
||||
│ └──example/ # 示例路由
|
||||
├── service/ # 服务层
|
||||
│ ├── enter.go # 服务组入口
|
||||
│ ├── system/ # 系统服务
|
||||
│ └── example/ # 示例服务
|
||||
├── source/ # 数据初始化
|
||||
├── utils/ # 工具包
|
||||
├── config.yaml # 配置文件
|
||||
└── main.go # 程序入口
|
||||
```
|
||||
|
||||
### **前端目录结构 (web/)**
|
||||
|
||||
```
|
||||
web/
|
||||
├── public/ # 静态资源
|
||||
├── src/
|
||||
│ ├── api/ # API接口定义
|
||||
│ │ ├── user.js # 用户相关API
|
||||
│ │ ├── menu.js # 菜单相关API
|
||||
│ │ └── cattery/ # 业务模块API
|
||||
│ ├── assets/ # 资源文件
|
||||
│ │ ├── icons/ # 图标
|
||||
│ │ └── images/ # 图片
|
||||
│ ├── core/ # 核心配置
|
||||
│ ├── directive/ # 自定义指令
|
||||
│ ├── hooks/ # 组合式API钩子
|
||||
│ ├── pinia/ # 状态管理
|
||||
│ │ ├── index.js # Pinia入口
|
||||
│ │ └── modules/ # 状态模块
|
||||
│ ├── plugin/ # 前端插件
|
||||
│ │ ├── announcement/ # 公告插件
|
||||
│ │ └── email/ # 邮件插件
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── style/ # 样式文件
|
||||
│ ├── utils/ # 工具函数
|
||||
│ ├── view/ # 页面组件
|
||||
│ │ ├── dashboard/ # 仪表盘
|
||||
│ │ ├── layout/ # 布局组件
|
||||
│ │ ├── login/ # 登录页
|
||||
│ │ ├── superAdmin/ # 超级管理员
|
||||
│ │ ├── systemTools/ # 系统工具
|
||||
│ │ └── cattery/ # 业务页面
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.js # 程序入口
|
||||
├── package.json # 依赖配置
|
||||
├── vite.config.js # Vite配置
|
||||
└── uno.config.js # UnoCSS配置
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 后端规则
|
||||
|
||||
在编写任何代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
|
||||
|
||||
1. **严格的分层架构**:
|
||||
|
||||
- **职责单一**: 每个层(Model, Service, API, Router)都有其唯一职责,**严禁跨层调用**。例如,API层绝不能直接操作数据库,必须通过Service层。Service层绝不能直接处理`gin.Context`。
|
||||
|
||||
- **依赖关系**: 依赖链条必须是单向的:`Router -> API -> Service -> Model`。
|
||||
|
||||
2. **`enter.go` 组管理模式**:
|
||||
|
||||
- 所有 `api`, `service`, `router` 层都**必须**使用 `enter.go` 文件来创建和暴露各自的 `ApiGroup`, `ServiceGroup`, `RouterGroup`。
|
||||
|
||||
- 全局实例变量(如 `service.ServiceGroupApp`)是模块间通信的唯一入口,以此来避免循环引用。
|
||||
|
||||
3. **详尽的 Swagger 注释 (API层强制要求)**:
|
||||
|
||||
- **每一个**对外暴露的 API 函数都**必须**拥有完整且准确的 Swagger 注释块。这不仅是API文档的来源,也是前后端协作、自动化测试和前端AI分析的基础。注释必须清晰地描述接口的功能、参数和返回值。
|
||||
|
||||
4. **统一的响应与错误处理**:
|
||||
|
||||
- Service 层函数遇到业务错误时,应返回 `error` 对象。
|
||||
|
||||
- API 层负责捕获 Service 层的 `error`,并使用项目统一的 `response` 包(如 `response.OkWithDetailed` 或 `response.FailWithMessage`)将其转换为格式化的 JSON 响应和正确的 HTTP 状态码。
|
||||
|
||||
|
||||
---
|
||||
|
||||
### **各层级代码实现规范**
|
||||
|
||||
#### **1. 模型层 (`model/`)**
|
||||
|
||||
- **数据模型 (`model/xxx.go`)**:
|
||||
|
||||
- 用于定义与数据库表映射的 GORM 结构体。
|
||||
|
||||
- 结构体应继承 `global.GVA_MODEL` 以包含 `ID`, `CreatedAt`, `UpdatedAt` 等基础字段。
|
||||
|
||||
- 必须为字段添加清晰的 `json` 和 `gorm` 标签。
|
||||
|
||||
- **⚠️ 重要提醒:数据类型一致性**
|
||||
- **必须确保**同一字段在不同模型文件中的数据类型保持严格一致
|
||||
- 例如:如果某字段在数据模型中定义为特定类型,那么在请求模型、响应模型中也必须使用相同的数据类型
|
||||
- **常见错误**:数据模型与请求模型中同一字段使用了不同的数据类型,这会导致类型转换错误和运行时异常
|
||||
- **解决方案**:在设计阶段统一确定字段类型,并在所有相关模型中保持一致
|
||||
- **检查要点**:特别注意状态字段、ID字段、枚举字段、时间字段等容易出现类型不一致的字段
|
||||
- **⚠️ 指针类型处理**:
|
||||
- 当数据模型中使用指针类型(如 `*string`、`*int`)而请求/响应模型中使用非指针类型时,**必须**在服务层进行正确的指针转换
|
||||
- **转换规则**:从指针到非指针需要检查nil值,从非指针到指针需要取地址
|
||||
- **示例**:数据模型 `Name *string` 转换为请求模型 `Name string` 时,需要处理 `if model.Name != nil { request.Name = *model.Name }`
|
||||
|
||||
- **请求模型 (`model/request/xxx.go`)**:
|
||||
|
||||
- 用于定义接收前端请求参数的结构体(DTOs)。
|
||||
|
||||
- **必须**为字段添加 `json` 和 `form` 标签,以便 Gin 进行参数绑定。
|
||||
|
||||
- 对于列表查询请求,应创建一个 `XxxSearch` 结构体,并内嵌通用的 `request.PageInfo` 分页结构体。
|
||||
|
||||
|
||||
#### **2. 服务层 (`service/`)**
|
||||
|
||||
- **职责**: 封装所有核心业务逻辑,进行数据库的CRUD操作。**此层不应出现任何与HTTP协议相关的代码(如 `gin.Context`)**。
|
||||
|
||||
- **结构**: 在 `service/` 下为每个模块创建 `xxx_service.go` 文件,并在 `service/enter.go` 中注册。
|
||||
|
||||
- **函数签名**: 函数应接收具体的业务参数(如 `model.Xxx` 或 `request.XxxSearch`),并返回处理结果和 `error`。
|
||||
|
||||
- **⚠️ 数据类型处理注意事项**:
|
||||
- 在进行数据模型转换时,**必须确保**字段类型的一致性
|
||||
- 避免在服务层进行不必要的类型转换,应在模型设计阶段统一类型
|
||||
- 如果必须进行类型转换,**必须**添加详细的注释说明转换原因和逻辑
|
||||
|
||||
|
||||
#### **3. API层 (`api/`)**
|
||||
|
||||
- **职责**: 作为HTTP请求的入口,负责参数校验、调用Service层方法、并返回格式化的JSON响应。
|
||||
|
||||
- **结构**: 在 `api/` 下为每个模块创建 `xxx_api.go` 文件,并在 `api/enter.go` 中注册。
|
||||
|
||||
- **交互**: **必须**通过全局变量 `service.ServiceGroupApp` 来调用服务层的方法。
|
||||
|
||||
- **Swagger 示例 (必须遵循)**:
|
||||
|
||||
Go
|
||||
|
||||
```
|
||||
// CreateXxx 创建XXX
|
||||
// @Tags XxxModule
|
||||
// @Summary 创建一个新的XXX
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CreateXxxRequest true "XXX的名称和描述"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /xxx/createXxx [post]
|
||||
func (a *XxxApi) CreateXxx(c *gin.Context) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### **4. 路由层 (`router/`)**
|
||||
|
||||
- **职责**: 定义API路由规则,并将HTTP请求路径映射到具体的API处理函数上,同时配置中间件。
|
||||
|
||||
- **结构**: 在 `router/` 下为每个模块创建 `xxx_router.go` 文件,并在 `router/enter.go` 中注册。
|
||||
|
||||
- **交互**: **必须**通过全局变量 `api.ApiGroupApp` 来引用API层的处理函数。
|
||||
|
||||
- **路由分组**: 应根据业务需求和权限,合理使用路由组 (`Router.Group()`),并挂载不同的中间件(如鉴权、操作记录等)。
|
||||
|
||||
#### **5. 初始化层 (`initialize/`)**
|
||||
|
||||
- **职责**: 提供插件资源(数据库、路由、菜单等)的初始化入口,供主程序调用。
|
||||
|
||||
- **`gorm.go`**: 实现 `InitializeDB` 函数,**必须**调用 `db.AutoMigrate` 自动迁移本插件所有 `model` 的表结构。
|
||||
|
||||
- **`router.go`**: 实现 `InitializeRouter` 函数,**必须**调用 `router.RouterGroupApp` 中本插件路由的初始化方法,注册所有API路由。
|
||||
|
||||
- **`menu.go`**: 实现 `InitializeMenu` 函数,负责在数据库中创建或更新本插件的侧边栏菜单、按钮和对应的API权限。
|
||||
- viper.go: 加载插件配置文件
|
||||
- api.go: 注册API到系统
|
||||
|
||||
|
||||
#### **6. 插件入口 (`plugin.go`)
|
||||
|
||||
- **职责**: 作为插件的唯一入口,实现 GVA 的插件接口,让框架能够识别和加载本插件。
|
||||
|
||||
- **接口实现**: **必须**定义一个结构体并实现 `system.Plugin` 接口。
|
||||
|
||||
- **`Register`方法**: 实现 `Register` 方法,该方法接收一个 `*gin.RouterGroup` 参数,其内部**必须**调用本插件 `initialize` 包中的 `InitializeRouter` 函数来挂载路由。
|
||||
|
||||
- **`RouterPath`方法**: 实现 `RouterPath` 方法,返回该插件所有API的根路径,例如 `"/myPlugin"`。
|
||||
|
||||
### 模块间引用关系:
|
||||
- API层引用Service层:在API文件中定义变量如 `var xxxService = service.ServiceGroupApp.XxxService`
|
||||
- Router层引用API层:在路由函数中使用 `api.ApiGroupApp.XxxApi.XxxMethod`
|
||||
- Initialize/Router引用Router层:通过 `router.RouterGroupApp.XxxRouter.InitXxxRouter`
|
||||
- 各模块通过enter.go文件组织和暴露功能,避免循环引用
|
||||
|
||||
### 代码组织示例:
|
||||
|
||||
1. Service入口 (service/enter.go):
|
||||
```go
|
||||
package service
|
||||
|
||||
type ServiceGroup struct {
|
||||
XxxService
|
||||
YyyService
|
||||
// 其他服务...
|
||||
}
|
||||
|
||||
var ServiceGroupApp = new(ServiceGroup)
|
||||
```
|
||||
|
||||
2. API入口 (api/enter.go):
|
||||
```go
|
||||
package api
|
||||
|
||||
type ApiGroup struct {
|
||||
XxxApi
|
||||
YyyApi
|
||||
// 其他API...
|
||||
}
|
||||
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
```
|
||||
|
||||
3. Router入口 (router/enter.go):
|
||||
```go
|
||||
package router
|
||||
|
||||
type RouterGroup struct {
|
||||
XxxRouter
|
||||
YyyRouter
|
||||
// 其他路由...
|
||||
}
|
||||
|
||||
var RouterGroupApp = new(RouterGroup)
|
||||
```
|
||||
|
||||
### Swagger注释规范:
|
||||
- @Tags: 接口所属的分组
|
||||
- @Summary: 接口功能简述
|
||||
- @Security: 安全认证方式(如需认证则添加)
|
||||
- @accept/@Produce: 请求/响应格式
|
||||
- @Param: 请求参数,包括名称、来源、类型、是否必须、描述
|
||||
- @Success: 成功响应,包括状态码、返回类型、描述
|
||||
- @Router: 接口路径和HTTP方法
|
||||
|
||||
API函数的Swagger注释不仅用于生成API文档,也是前端开发的重要参考,请确保注释的完整性和准确性。
|
||||
|
||||
|
||||
---
|
||||
|
||||
### **开发工作流**
|
||||
|
||||
1. **接收任务**: 我会向你下达一个具体的功能插件开发任务,例如:“请为项目创建一个‘商品管理 (Product)’插件”。
|
||||
|
||||
2. **【第一步】模型设计 (奠定基础)**:
|
||||
|
||||
- 你的**首要行动**是分析需求,设计并提供 `model` 和 `model/request` 下的所有 Go 结构体定义。这是后续所有开发的基础。
|
||||
|
||||
3. **【第二步】自下而上,分层实现**:
|
||||
- 具体项目结构可以参考:server/plugin/announcement 这个插件,非常经典!
|
||||
|
||||
- 在模型确认后,你将按照 `Service -> API -> Router` 的顺序,逐层生成代码。
|
||||
|
||||
- 确保每一层的代码都完整、健壮,并严格遵守上述规范。
|
||||
|
||||
4. **【第三步】插件初始化与注册**:
|
||||
|
||||
- 在完成核心功能层的代码后,你将生成 `initialize/` 目录下的相关初始化文件(如 `db.go`, `router.go`)以及插件的主入口文件 `plugin.go`。
|
||||
|
||||
5. **【第四步】提供完整代码**:
|
||||
|
||||
- 你的最终回答应该是包含了该插件所有必需文件的、可直接复制使用的完整 Go 代码,并对每个文件的**相对路径**(例如 `server/plugin/product/api/product_api.go`)和用途进行清晰的说明。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## **前端开发规范**
|
||||
|
||||
### **角色与目标**
|
||||
|
||||
你是一名资深的 Vue.js 前端开发专家,**专精于 `gin-vue-admin` (GVA) 框架的前端架构与开发范式**。
|
||||
|
||||
你的核心任务是,根据需求开发**完整、生产级别的前端功能模块或插件**。你必须严格遵循 GVA 的前端架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
|
||||
|
||||
### **核心开发指令:绝不可违背的原则**
|
||||
|
||||
#### 前端规则
|
||||
|
||||
在编写任何前端代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
|
||||
|
||||
1. **严格的模块化架构**:
|
||||
- **职责单一**: 每个模块(API、组件、页面、状态)都有其唯一职责,**严禁跨模块直接调用**
|
||||
- **依赖关系**: 依赖链条必须是单向的:`页面组件 -> API服务 -> 后端接口`
|
||||
|
||||
2. **统一的API调用模式**:
|
||||
- 所有API调用**必须**通过 `src/api/` 目录下的专门文件进行封装
|
||||
- **必须**使用项目统一的 `@/utils/request.js` 进行HTTP请求
|
||||
- API函数**必须**包含完整的JSDoc注释,描述接口功能、参数和返回值
|
||||
|
||||
3. **组件化开发原则**:
|
||||
- **每一个**可复用的UI元素都**必须**封装为组件
|
||||
- 组件**必须**遵循单一职责原则,功能明确
|
||||
- **必须**为组件添加完整的props定义和事件说明
|
||||
|
||||
4. **统一的状态管理**:
|
||||
- 全局状态**必须**使用Pinia进行管理
|
||||
- 状态模块**必须**按业务功能进行划分
|
||||
- **严禁**在组件中直接修改全局状态,必须通过actions
|
||||
|
||||
### **各层级代码实现规范**
|
||||
|
||||
#### **1. API层 (`src/api/`)**
|
||||
|
||||
- **职责**: 封装所有后端API调用,提供统一的接口服务
|
||||
- **结构**: 按业务模块创建API文件,如 `user.js`、`menu.js`
|
||||
- **规范**:
|
||||
```javascript
|
||||
import service from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* @param {Object} data 查询参数
|
||||
* @param {number} data.page 页码
|
||||
* @param {number} data.pageSize 每页数量
|
||||
* @returns {Promise} 用户列表数据
|
||||
*/
|
||||
export const getUserList = (data) => {
|
||||
return service({
|
||||
url: '/user/getUserList',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
#### **2. 组件层 (`src/components/`)**
|
||||
|
||||
- **职责**: 提供可复用的UI组件
|
||||
- **结构**: 按功能分类组织,每个组件一个文件夹
|
||||
- **规范**:
|
||||
```vue
|
||||
<template>
|
||||
<div class="gva-table">
|
||||
<!-- 组件内容 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 通用表格组件
|
||||
* @component GvaTable
|
||||
* @description 提供统一的表格展示功能
|
||||
*/
|
||||
|
||||
// Props定义
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
// 事件定义
|
||||
const emit = defineEmits(['refresh', 'edit', 'delete'])
|
||||
</script>
|
||||
```
|
||||
|
||||
#### **3. 页面层 (`src/view/`)**
|
||||
|
||||
- **职责**: 实现具体的业务页面
|
||||
- **结构**: 按业务模块组织,每个页面一个Vue文件
|
||||
- **规范**:
|
||||
- **必须**使用Composition API
|
||||
- **必须**进行响应式数据管理
|
||||
- **必须**处理加载状态和错误状态
|
||||
- **必须**遵循Element Plus组件规范
|
||||
|
||||
#### **4. 状态管理 (`src/pinia/`)**
|
||||
|
||||
- **职责**: 管理全局状态和业务逻辑
|
||||
- **结构**: 按业务模块创建store文件
|
||||
- **规范**:
|
||||
```javascript
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
// 状态定义 - 使用 ref() 创建响应式状态
|
||||
const userInfo = ref({
|
||||
uuid: '',
|
||||
nickName: '',
|
||||
headerImg: '',
|
||||
authority: {}
|
||||
})
|
||||
const token = useStorage('token', '')
|
||||
|
||||
// 计算属性 - 使用 computed() 定义
|
||||
const isLogin = computed(() => !!token.value)
|
||||
|
||||
// 方法定义 - 直接定义函数作为 actions
|
||||
const setUserInfo = (val) => {
|
||||
userInfo.value = val
|
||||
}
|
||||
|
||||
const setToken = (val) => {
|
||||
token.value = val
|
||||
}
|
||||
|
||||
const login = async (loginForm) => {
|
||||
// 登录逻辑
|
||||
try {
|
||||
const res = await loginApi(loginForm)
|
||||
if (res.code === 0) {
|
||||
setUserInfo(res.data.user)
|
||||
setToken(res.data.token)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} catch (error) {
|
||||
console.error('Login error:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
// 登出逻辑
|
||||
token.value = ''
|
||||
userInfo.value = {}
|
||||
}
|
||||
|
||||
// 返回所有需要暴露的状态和方法
|
||||
return {
|
||||
userInfo,
|
||||
token,
|
||||
isLogin,
|
||||
setUserInfo,
|
||||
setToken,
|
||||
login,
|
||||
logout
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### **5. 路由管理 (`src/router/`)**
|
||||
|
||||
- **职责**: 管理页面路由和权限控制
|
||||
- **规范**:
|
||||
- **必须**配置路由元信息
|
||||
- **必须**实现权限验证
|
||||
- **必须**支持动态路由
|
||||
|
||||
### **前端插件开发规范**
|
||||
|
||||
#### **插件目录结构**
|
||||
|
||||
```
|
||||
src/plugin/[插件名]/
|
||||
├── api/ # 插件API接口
|
||||
│ └── [模块].js
|
||||
├── components/ # 插件组件(可选)
|
||||
│ └── [组件名].vue
|
||||
├── view/ # 插件页面
|
||||
│ └── [页面名].vue
|
||||
├── form/ # 插件表单(可选)
|
||||
│ └── [表单名].vue
|
||||
└── index.js # 插件入口文件(可选)
|
||||
```
|
||||
|
||||
#### **插件开发原则**
|
||||
|
||||
1. **独立性**: 插件应该是自包含的,不依赖其他业务模块
|
||||
2. **可配置性**: 插件应该支持配置化,便于定制
|
||||
3. **可扩展性**: 插件应该预留扩展接口
|
||||
4. **一致性**: 插件UI风格应与主系统保持一致
|
||||
|
||||
### **代码质量要求**
|
||||
|
||||
1. **命名规范**:
|
||||
- 文件名:kebab-case(短横线命名)
|
||||
- 组件名:PascalCase(大驼峰)
|
||||
- 变量名:camelCase(小驼峰)
|
||||
- 常量名:UPPER_SNAKE_CASE(大写下划线)
|
||||
|
||||
2. **注释规范**:
|
||||
- **必须**为所有API函数添加JSDoc注释
|
||||
- **必须**为复杂组件添加功能说明
|
||||
- **必须**为关键业务逻辑添加行内注释
|
||||
|
||||
3. **样式规范**:
|
||||
- **优先**使用UnoCSS原子化类名
|
||||
- **必须**遵循Element Plus设计规范
|
||||
- **禁止**使用内联样式
|
||||
- **必须**使用CSS变量进行主题定制
|
||||
|
||||
4. **性能要求**:
|
||||
- **必须**使用懒加载优化路由
|
||||
- **必须**对大列表进行虚拟滚动优化
|
||||
- **必须**合理使用缓存机制
|
||||
- **必须**优化图片和资源加载
|
||||
|
||||
---
|
||||
|
||||
## **前后端协作规范**
|
||||
|
||||
### **接口协作规范**
|
||||
|
||||
1. **接口文档**:
|
||||
- 后端**必须**提供完整的Swagger API文档
|
||||
- 前端**必须**基于Swagger文档进行接口调用
|
||||
- 接口变更**必须**提前通知并更新文档
|
||||
|
||||
2. **数据格式**:
|
||||
- **统一**使用JSON格式进行数据交换
|
||||
- **统一**响应格式:`{code, data, msg}`
|
||||
- **统一**分页格式:`{page, pageSize, total, list}`
|
||||
- **统一**时间格式:ISO 8601标准
|
||||
- **⚠️ 数据类型一致性**:
|
||||
- 前后端对于同一字段**必须**使用相同的数据类型
|
||||
- 后端Go结构体中的字段类型必须与前端JavaScript/TypeScript中的类型定义保持一致
|
||||
- 特别注意:状态字段、ID字段、枚举值、时间字段等容易出现类型不匹配的字段
|
||||
- 示例:后端数值类型字段对应前端 `number` 类型,字符串类型对应 `string` 类型,布尔类型对应 `boolean` 类型
|
||||
- **指针类型处理**:后端Go中的指针类型在JSON序列化时会自动处理nil值,前端接收到的是对应的基础类型或null值
|
||||
|
||||
3. **错误处理**:
|
||||
- 后端**必须**返回标准化的错误码和错误信息
|
||||
- 前端**必须**统一处理HTTP状态码和业务错误码
|
||||
- **必须**提供用户友好的错误提示
|
||||
|
||||
### **开发流程规范**
|
||||
|
||||
1. **需求分析阶段**:
|
||||
- 确定功能需求和接口设计
|
||||
- 定义数据模型和业务流程
|
||||
- 制定前后端开发计划
|
||||
|
||||
2. **开发阶段**:
|
||||
- 后端优先开发API接口
|
||||
- 前端基于Mock数据进行并行开发
|
||||
- 定期进行接口联调测试
|
||||
|
||||
3. **测试阶段**:
|
||||
- 单元测试:前后端各自负责
|
||||
- 集成测试:前后端协作完成
|
||||
- 用户验收测试:产品团队主导
|
||||
|
||||
### **版本管理规范**
|
||||
|
||||
1. **分支策略**:
|
||||
- `main`:生产环境分支
|
||||
- `develop`:开发环境分支
|
||||
- `feature/*`:功能开发分支
|
||||
- `hotfix/*`:紧急修复分支
|
||||
|
||||
2. **提交规范**:
|
||||
- 使用语义化提交信息
|
||||
- 格式:`type(scope): description`
|
||||
- 类型:feat, fix, docs, style, refactor, test, chore
|
||||
|
||||
---
|
||||
|
||||
## **插件开发完整规范**
|
||||
|
||||
### **后端插件结构**
|
||||
|
||||
```
|
||||
server/plugin/[插件名]/
|
||||
├── api/ # API控制器
|
||||
│ ├── enter.go # API组入口
|
||||
│ └── [模块].go # 具体API实现
|
||||
├── config/ # 插件配置
|
||||
│ └── config.go
|
||||
├── initialize/ # 初始化模块
|
||||
│ ├── api.go # API注册
|
||||
│ ├── gorm.go # 数据库初始化
|
||||
│ ├── menu.go # 菜单初始化
|
||||
│ ├── router.go # 路由初始化
|
||||
│ └── viper.go # 配置初始化
|
||||
├── model/ # 数据模型
|
||||
│ ├── [模型].go # 数据库模型
|
||||
│ └── request/ # 请求模型
|
||||
├── router/ # 路由定义
|
||||
│ ├── enter.go # 路由组入口
|
||||
│ └── [模块].go # 具体路由
|
||||
├── service/ # 业务服务
|
||||
│ ├── enter.go # 服务组入口
|
||||
│ └── [模块].go # 具体服务
|
||||
└── plugin.go # 插件入口
|
||||
```
|
||||
|
||||
### **前端插件结构**
|
||||
|
||||
```
|
||||
web/src/plugin/[插件名]/
|
||||
├── api/ # API接口
|
||||
│ └── [模块].js
|
||||
├── components/ # 插件组件
|
||||
│ └── [组件].vue
|
||||
├── view/ # 插件页面
|
||||
│ └── [页面].vue
|
||||
├── form/ # 表单组件
|
||||
│ └── [表单].vue
|
||||
└── config.js # 插件配置
|
||||
```
|
||||
|
||||
### **插件开发工作流**
|
||||
|
||||
1. **【第一步】需求分析**:
|
||||
- 明确插件功能和业务需求
|
||||
- 设计数据模型和接口规范
|
||||
- 规划前端页面和交互流程
|
||||
|
||||
2. **【第二步】后端开发**:
|
||||
- 创建数据模型和请求模型
|
||||
- 实现服务层业务逻辑
|
||||
- 开发API控制器和路由
|
||||
- 编写初始化和配置代码
|
||||
|
||||
3. **【第三步】前端开发**:
|
||||
- 创建API接口封装
|
||||
- 开发页面组件和表单
|
||||
- 实现业务逻辑和状态管理
|
||||
- 集成到主系统菜单
|
||||
|
||||
4. **【第四步】测试集成**:
|
||||
- 单元测试和集成测试
|
||||
- 前后端联调测试
|
||||
- 用户体验测试
|
||||
- 性能和安全测试
|
||||
|
||||
### **插件质量标准**
|
||||
|
||||
1. **功能完整性**: 插件功能完整,满足业务需求
|
||||
2. **代码质量**: 代码规范,注释完整,易于维护
|
||||
3. **数据类型一致性**: 前后端数据模型字段类型保持严格一致,避免类型转换错误
|
||||
4. **性能表现**: 响应速度快,资源占用合理
|
||||
5. **用户体验**: 界面友好,操作流畅,错误处理完善
|
||||
6. **兼容性**: 与主系统兼容,不影响其他功能
|
||||
7. **安全性**: 数据安全,权限控制,防止安全漏洞
|
||||
|
||||
---
|
||||
|
||||
### **建议和方案**
|
||||
|
||||
基于以上规范,建议AI在开发gin-vue-admin项目时:
|
||||
|
||||
1. **严格遵循分层架构**:确保前后端代码都按照规定的层次结构组织
|
||||
2. **保持代码一致性**:使用统一的命名规范、注释格式和代码风格
|
||||
3. **注重文档完整性**:确保API文档、代码注释和使用说明的完整性
|
||||
4. **优化用户体验**:关注页面加载速度、交互流畅性和错误处理
|
||||
5. **考虑扩展性**:设计时预留扩展接口,便于后续功能增强
|
||||
6. **重视安全性**:实现完善的权限控制和数据验证机制
|
||||
@@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2019 JiZhao Jiang
|
||||
Copyright 2019 北京翻转极光科技有限责任公司
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
SHELL = /bin/bash
|
||||
|
||||
#SCRIPT_DIR = $(shell pwd)/etc/script
|
||||
#请选择golang版本
|
||||
BUILD_IMAGE_SERVER = golang:1.22
|
||||
#请选择node版本
|
||||
BUILD_IMAGE_WEB = node:20
|
||||
#项目名称
|
||||
PROJECT_NAME = github.com/flipped-aurora/gin-vue-admin/server
|
||||
#配置文件目录
|
||||
CONFIG_FILE = config.yaml
|
||||
#镜像仓库命名空间
|
||||
IMAGE_NAME = gva
|
||||
#镜像地址
|
||||
REPOSITORY = registry.cn-hangzhou.aliyuncs.com/${IMAGE_NAME}
|
||||
#镜像版本
|
||||
TAGS_OPT ?= latest
|
||||
PLUGIN ?= email
|
||||
|
||||
#容器环境前后端共同打包
|
||||
build: build-web build-server
|
||||
docker run --name build-local --rm -v $(shell pwd):/go/src/${PROJECT_NAME} -w /go/src/${PROJECT_NAME} ${BUILD_IMAGE_SERVER} make build-local
|
||||
|
||||
#容器环境打包前端
|
||||
build-web:
|
||||
docker run --name build-web-local --rm -v $(shell pwd):/go/src/${PROJECT_NAME} -w /go/src/${PROJECT_NAME} ${BUILD_IMAGE_WEB} make build-web-local
|
||||
|
||||
#容器环境打包后端
|
||||
build-server:
|
||||
docker run --name build-server-local --rm -v $(shell pwd):/go/src/${PROJECT_NAME} -w /go/src/${PROJECT_NAME} ${BUILD_IMAGE_SERVER} make build-server-local
|
||||
|
||||
#构建web镜像
|
||||
build-image-web:
|
||||
@cd web/ && docker build -t ${REPOSITORY}/web:${TAGS_OPT} .
|
||||
|
||||
#构建server镜像
|
||||
build-image-server:
|
||||
@cd server/ && docker build -t ${REPOSITORY}/server:${TAGS_OPT} .
|
||||
|
||||
#本地环境打包前后端
|
||||
build-local:
|
||||
if [ -d "build" ];then rm -rf build; else echo "OK!"; fi \
|
||||
&& if [ -f "/.dockerenv" ];then echo "OK!"; else make build-web-local && make build-server-local; fi \
|
||||
&& mkdir build && cp -r web/dist build/ && cp server/server build/ && cp -r server/resource build/resource
|
||||
|
||||
#本地环境打包前端
|
||||
build-web-local:
|
||||
@cd web/ && if [ -d "dist" ];then rm -rf dist; else echo "OK!"; fi \
|
||||
&& yarn config set registry http://mirrors.cloud.tencent.com/npm/ && yarn install && yarn build
|
||||
|
||||
#本地环境打包后端
|
||||
build-server-local:
|
||||
@cd server/ && if [ -f "server" ];then rm -rf server; else echo "OK!"; fi \
|
||||
&& go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct \
|
||||
&& go env -w CGO_ENABLED=0 && go env && go mod tidy \
|
||||
&& go build -ldflags "-B 0x$(shell head -c20 /dev/urandom|od -An -tx1|tr -d ' \n') -X main.Version=${TAGS_OPT}" -v
|
||||
|
||||
#打包前后端二合一镜像
|
||||
image: build
|
||||
docker build -t ${REPOSITORY}/gin-vue-admin:${TAGS_OPT} -f deploy/docker/Dockerfile .
|
||||
|
||||
#尝鲜版
|
||||
images: build build-image-web build-image-server
|
||||
docker build -t ${REPOSITORY}/all:${TAGS_OPT} -f deploy/docker/Dockerfile .
|
||||
|
||||
#swagger 文档生成
|
||||
doc:
|
||||
@cd server && swag init
|
||||
|
||||
#插件快捷打包: make plugin PLUGIN="这里是插件文件夹名称,默认为email"
|
||||
plugin:
|
||||
if [ -d ".plugin" ];then rm -rf .plugin ; else echo "OK!"; fi && mkdir -p .plugin/${PLUGIN}/{server/plugin,web/plugin} \
|
||||
&& if [ -d "server/plugin/${PLUGIN}" ];then cp -r server/plugin/${PLUGIN} .plugin/${PLUGIN}/server/plugin/ ; else echo "OK!"; fi \
|
||||
&& if [ -d "web/src/plugin/${PLUGIN}" ];then cp -r web/src/plugin/${PLUGIN} .plugin/${PLUGIN}/web/plugin/ ; else echo "OK!"; fi \
|
||||
&& cd .plugin && zip -r ${PLUGIN}.zip ${PLUGIN} && mv ${PLUGIN}.zip ../ && cd ..
|
||||
+13
-13
@@ -1,13 +1,13 @@
|
||||
|
||||
<div align=center>
|
||||
<img src="http://qmplusimg.henrongyi.top/gvalogo.jpg" width=300" height="300" />
|
||||
<img src="http://qmplusimg.henrongyi.top/gvalogo.jpg" width="300" height="300" />
|
||||
</div>
|
||||
<div align=center>
|
||||
<img src="https://img.shields.io/badge/golang-1.14-blue"/>
|
||||
<img src="https://img.shields.io/badge/gin-1.6.3-lightBlue"/>
|
||||
<img src="https://img.shields.io/badge/vue-2.6.10-brightgreen"/>
|
||||
<img src="https://img.shields.io/badge/element--ui-2.12.0-green"/>
|
||||
<img src="https://img.shields.io/badge/gorm-1.20.7-red"/>
|
||||
<img src="https://img.shields.io/badge/golang-1.18-blue"/>
|
||||
<img src="https://img.shields.io/badge/gin-1.9.1-lightBlue"/>
|
||||
<img src="https://img.shields.io/badge/vue-3.3.4-brightgreen"/>
|
||||
<img src="https://img.shields.io/badge/element--plus-2.3.8-green"/>
|
||||
<img src="https://img.shields.io/badge/gorm-1.25.2-red"/>
|
||||
</div>
|
||||
|
||||
English | [简体中文](./README.md)
|
||||
@@ -16,16 +16,12 @@ English | [简体中文](./README.md)
|
||||
|
||||
[github](https://github.com/flipped-aurora/gin-vue-admin): https://github.com/flipped-aurora/gin-vue-admin
|
||||
|
||||
[Vue3 version branch address](https://github.com/flipped-aurora/gin-vue-admin/tree/vue3Develop): https://github.com/flipped-aurora/gin-vue-admin/tree/vue3Develop
|
||||
|
||||
[Approval flow branch](https://github.com/flipped-aurora/gin-vue-admin/tree/gva_workflow): https://github.com/flipped-aurora/gin-vue-admin/tree/gva_workflow
|
||||
|
||||
# Project Guidelines
|
||||
[Online Documentation](https://www.gin-vue-admin.com/) : https://www.gin-vue-admin.com/
|
||||
|
||||
[From the environment to the deployment of teaching videos](https://www.bilibili.com/video/BV1fV411y7dT)
|
||||
|
||||
[Development Steps](https://www.gin-vue-admin.com/docs/help) (Contributor: <a href="https://github.com/LLemonGreen">LLemonGreen</a> And <a href="https://github.com/fkk0509">Fann</a>)
|
||||
[Development Steps](https://www.gin-vue-admin.com/guide/start-quickly/env.html) (Contributor: <a href="https://github.com/LLemonGreen">LLemonGreen</a> And <a href="https://github.com/fkk0509">Fann</a>)
|
||||
|
||||
## 1. Basic Introduction
|
||||
|
||||
@@ -297,19 +293,23 @@ swag init
|
||||
| :---: |
|
||||
| <img width="150" src="http://qmplusimg.henrongyi.top/qrjjz.png">
|
||||
|
||||
#### [About Us](https://www.gin-vue-admin.com/about/)
|
||||
#### [About Us](https://www.gin-vue-admin.com/about/join.html)
|
||||
|
||||
## 8. Contributors
|
||||
|
||||
Thank you for considering your contribution to gin-vue-admin!
|
||||
|
||||
<a href="https://openomy.app/github/flipped-aurora/gin-vue-admin" target="_blank" style="display: block; width: 100%;" align="center">
|
||||
<img src="https://openomy.app/svg?repo=flipped-aurora/gin-vue-admin&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/flipped-aurora/gin-vue-admin/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=flipped-aurora/gin-vue-admin" />
|
||||
</a>
|
||||
|
||||
## 9. Donate
|
||||
|
||||
If you find this project useful, you can buy author a glass of juice :tropical_drink: [here](https://www.gin-vue-admin.com/docs/coffee)
|
||||
If you find this project useful, you can buy author a glass of juice :tropical_drink: [here](https://www.gin-vue-admin.com/coffee/index.html)
|
||||
|
||||
## 10. Commercial considerations
|
||||
|
||||
|
||||
@@ -1,49 +1,71 @@
|
||||
|
||||
<div align=center>
|
||||
<img src="http://qmplusimg.henrongyi.top/gvalogo.jpg" width=300" height="300" />
|
||||
<img src="http://qmplusimg.henrongyi.top/gvalogo.jpg" width="300" height="300" />
|
||||
</div>
|
||||
|
||||
<div align=center>
|
||||
<img src="https://img.shields.io/badge/golang-1.16-blue"/>
|
||||
<img src="https://img.shields.io/badge/gin-1.6.3-lightBlue"/>
|
||||
<img src="https://img.shields.io/badge/vue-3.0.0-brightgreen"/>
|
||||
<img src="https://img.shields.io/badge/element--plus-1.1.0beta8-green"/>
|
||||
<img src="https://img.shields.io/badge/gorm-1.20.7-red"/>
|
||||
<img src="https://img.shields.io/badge/golang-1.20-blue"/>
|
||||
<img src="https://img.shields.io/badge/gin-1.9.1-lightBlue"/>
|
||||
<img src="https://img.shields.io/badge/vue-3.3.4-brightgreen"/>
|
||||
<img src="https://img.shields.io/badge/element--plus-2.3.8-green"/>
|
||||
<img src="https://img.shields.io/badge/gorm-1.25.2-red"/>
|
||||
<img src="https://gitcode.com/flipped-aurora/gin-vue-admin/star/badge.svg"/>
|
||||
</div>
|
||||
|
||||
<div align=center>
|
||||
<a href="https://trendshift.io/repositories/3250" target="_blank"><img src="https://trendshift.io/api/badge/repositories/3250" alt="Calcium-Ion%2Fnew-api | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</div>
|
||||
|
||||
[English](./README-en.md) | 简体中文
|
||||
|
||||
[gitee地址](https://gitee.com/pixelmax/gin-vue-admin): https://gitee.com/pixelmax/gin-vue-admin
|
||||
## ✨一分钟生成前后端基础代码
|
||||
|
||||
[github地址](https://github.com/flipped-aurora/gin-vue-admin): https://github.com/flipped-aurora/gin-vue-admin
|
||||
<table>
|
||||
<tr>
|
||||
<td width="250">
|
||||
<p>⭐️ <a href="https://www.bilibili.com/video/BV1B3htzqEf1/?spm_id_from=333.1387.homepage.video_card.click" target="__blank"> 高度适配AI编辑器的MCP </a></p>
|
||||
<p>📄 创建基础模板</p>
|
||||
<p>🤖 AI生成结构</p>
|
||||
<p>⏰ 生成代码</p>
|
||||
<p>🏷️ 分配权限</p>
|
||||
<p>🎉 基础CURD生成完成</p>
|
||||
</td>
|
||||
<td>
|
||||
<video src="https://private-user-images.githubusercontent.com/165128580/384700666-4d039215-af29-4f86-bb4f-60dbab38f58e.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzEyNTIxNDYsIm5iZiI6MTczMTI1MTg0NiwicGF0aCI6Ii8xNjUxMjg1ODAvMzg0NzAwNjY2LTRkMDM5MjE1LWFmMjktNGY4Ni1iYjRmLTYwZGJhYjM4ZjU4ZS5tcDQ_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQxMTEwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MTExMFQxNTE3MjZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT00NjJkMDcwZjJkMjAyMmU1N2I2MzQxY2RhODFlNzgzNGRiMDFhMmY2NTYyM2ZmODdhNDVmMWE1NzlhMDdlOTI5JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.ZJbswpLzF2RHjemcGirKOP0L1fvpl3FUqIiQ_-yjeUo" data-canonical-src="https://private-user-images.githubusercontent.com/165128580/384700666-4d039215-af29-4f86-bb4f-60dbab38f58e.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzEyNTIxNDYsIm5iZiI6MTczMTI1MTg0NiwicGF0aCI6Ii8xNjUxMjg1ODAvMzg0NzAwNjY2LTRkMDM5MjE1LWFmMjktNGY4Ni1iYjRmLTYwZGJhYjM4ZjU4ZS5tcDQ_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQxMTEwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MTExMFQxNTE3MjZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT00NjJkMDcwZjJkMjAyMmU1N2I2MzQxY2RhODFlNzgzNGRiMDFhMmY2NTYyM2ZmODdhNDVmMWE1NzlhMDdlOTI5JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.ZJbswpLzF2RHjemcGirKOP0L1fvpl3FUqIiQ_-yjeUo" controls="controls" muted="muted" class="d-block rounded-bottom-2 border-top width-fit" style="max-height:640px; min-height: 200px">
|
||||
</video>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
[插件仓库以及开发规范](https://github.com/flipped-aurora/gva-plugins):https://github.com/flipped-aurora/gva-plugins
|
||||
|
||||
[审批流分支](https://github.com/flipped-aurora/gin-vue-admin/tree/gva_workflow): https://github.com/flipped-aurora/gin-vue-admin/tree/gva_workflow
|
||||
|
||||
# 项目文档
|
||||
[在线文档](https://www.gin-vue-admin.com) : https://www.gin-vue-admin.com
|
||||
|
||||
[初始化](https://www.gin-vue-admin.com/docs/first_master)
|
||||
[初始化](https://www.gin-vue-admin.com/guide/start-quickly/initialization.html)
|
||||
|
||||
[从环境到部署教学视频](https://www.bilibili.com/video/BV1Rg411u7xH)
|
||||
|
||||
[开发教学](https://www.gin-vue-admin.com/docs/help) (贡献者: <a href="https://github.com/LLemonGreen">LLemonGreen</a> And <a href="https://github.com/fkk0509">Fann</a>)
|
||||
[开发教学](https://www.gin-vue-admin.com/guide/start-quickly/env.html) (贡献者: <a href="https://github.com/LLemonGreen">LLemonGreen</a> And <a href="https://github.com/fkk0509">Fann</a>)
|
||||
|
||||
[交流社区](https://support.qq.com/products/371961)
|
||||
|
||||
[插件市场](https://plugin.gin-vue-admin.com/)
|
||||
|
||||
[软件著作权证书](https://www.gin-vue-admin.com/copyright.pdf)
|
||||
|
||||
# 重要提示
|
||||
|
||||
1.本项目从起步到开发到部署均有文档和详细视频教程
|
||||
|
||||
2.本项目需要您有一定的golang和vue基础
|
||||
|
||||
3.您完全可以通过我们的教程和文档完成一切操作,因此我们不再提供免费的技术服务,如需服务请进行[付费支持](https://www.gin-vue-admin.com/docs/payment)
|
||||
3.您完全可以通过我们的教程和文档完成一切操作,因此我们不再提供免费的技术服务,如需服务请进行[付费支持](https://www.gin-vue-admin.com/coffee/payment.html)
|
||||
|
||||
4.如果您将此项目用于商业用途,请遵守Apache2.0协议并保留作者技术支持声明。您需保留如下版权声明信息,其余信息功能不做任何限制。如需剔除请联系微信:shouzi_1994
|
||||
4.如果您将此项目用于商业用途,请遵守Apache2.0协议并保留作者技术支持声明。您需保留如下版权声明信息,以及日志和代码中所包含的版权声明信息。所需保留信息均为文案性质,不会影响任何业务内容,如决定商用【产生收益的商业行为均在商用行列】或者必须剔除请[购买授权](https://www.gin-vue-admin.com/empower/index.html)
|
||||
\
|
||||
<img src="https://qmplusimg.henrongyi.top/openSource/login.jpg" width="1000">
|
||||
|
||||
<img src="https://qmplusimg.henrongyi.top/%E6%8E%88%E6%9D%83.png" width="1000">
|
||||
|
||||
5.如果您需要服务器的话 2C4G8M 80GB 腾讯云 一年74 三年222 在这里购买:https://curl.qcloud.com/sArxMfaw
|
||||
<img src="https://qmplusimg.henrongyi.top/openSource/dashboard.jpg" width="1000">
|
||||
|
||||
## 1. 基本介绍
|
||||
|
||||
@@ -65,8 +87,8 @@ Gin-vue-admin 是一套为快速研发准备的一整套前后端分离架构式
|
||||
Gin-vue-admin 的成长离不开大家的支持,如果你愿意为 gin-vue-admin 贡献代码或提供建议,请阅读以下内容。
|
||||
|
||||
#### 1.2.1 Issue 规范
|
||||
- issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。如果你在使用时产生了疑问,请到 Slack 或 [Gitter](https://gitter.im/ElemeFE/element) 里咨询。
|
||||
|
||||
- issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。
|
||||
|
||||
- 在提交 issue 之前,请搜索相关内容是否已被提出。
|
||||
|
||||
#### 1.2.2 Pull Request 规范
|
||||
@@ -74,8 +96,6 @@ Gin-vue-admin 的成长离不开大家的支持,如果你愿意为 gin-vue-adm
|
||||
|
||||
- commit 信息要以`[文件名]: 描述信息` 的形式填写,例如 `README.md: fix xxx bug`。
|
||||
|
||||
- <font color=red>确保 PR 是提交到 `develop` 分支,而不是 `master` 分支。</font>
|
||||
|
||||
- 如果是修复 bug,请在 PR 中给出描述信息。
|
||||
|
||||
- 合并代码需要两名维护人员参与:一人进行 review 后 approve,另一人再次 review,通过后即可合并。
|
||||
@@ -83,11 +103,9 @@ Gin-vue-admin 的成长离不开大家的支持,如果你愿意为 gin-vue-adm
|
||||
## 2. 使用说明
|
||||
|
||||
```
|
||||
- node版本 > v12.18.3
|
||||
- golang版本 >= v1.16
|
||||
- node版本 > v18.16.0
|
||||
- golang版本 >= v1.22
|
||||
- IDE推荐:Goland
|
||||
- 初始化项目: 不同版本数据库初始化不通 参见 https://www.gin-vue-admin.com/docs/first_master
|
||||
- 替换掉项目中的七牛云公钥,私钥,仓名和默认url地址,以免发生测试文件数据错乱
|
||||
```
|
||||
|
||||
### 2.1 server项目
|
||||
@@ -104,11 +122,9 @@ cd server
|
||||
# 使用 go mod 并安装go依赖包
|
||||
go generate
|
||||
|
||||
# 编译
|
||||
go build -o server main.go (windows编译命令为go build -o server.exe main.go )
|
||||
# 运行
|
||||
go run .
|
||||
|
||||
# 运行二进制
|
||||
./server (windows运行命令为 server.exe)
|
||||
```
|
||||
|
||||
### 2.2 web项目
|
||||
@@ -118,7 +134,7 @@ go build -o server main.go (windows编译命令为go build -o server.exe main.go
|
||||
cd web
|
||||
|
||||
# 安装依赖
|
||||
cnpm install || npm install
|
||||
npm install
|
||||
|
||||
# 启动web项目
|
||||
npm run serve
|
||||
@@ -128,29 +144,8 @@ npm run serve
|
||||
|
||||
#### 2.3.1 安装 swagger
|
||||
|
||||
##### (1)可以访问外国网站
|
||||
|
||||
````
|
||||
go get -u github.com/swaggo/swag/cmd/swag
|
||||
````
|
||||
|
||||
##### (2)无法访问外国网站
|
||||
|
||||
由于国内没法安装 go.org/x 包下面的东西,推荐使用 [goproxy.cn](https://goproxy.cn) 或者 [goproxy.io](https://goproxy.io/zh/)
|
||||
|
||||
```bash
|
||||
# 如果您使用的 Go 版本是 1.13 - 1.15 需要手动设置GO111MODULE=on, 开启方式如下命令, 如果你的 Go 版本 是 1.16 ~ 最新版 可以忽略以下步骤一
|
||||
# 步骤一、启用 Go Modules 功能
|
||||
go env -w GO111MODULE=on
|
||||
# 步骤二、配置 GOPROXY 环境变量
|
||||
go env -w GOPROXY=https://goproxy.cn,https://goproxy.io,direct
|
||||
|
||||
# 如果嫌弃麻烦,可以使用go generate 编译前自动执行代码, 不过这个不能使用 `Goland` 或者 `Vscode` 的 命令行终端
|
||||
cd server
|
||||
go generate -run "go env -w .*?"
|
||||
|
||||
# 使用如下命令下载swag
|
||||
go get -u github.com/swaggo/swag/cmd/swag
|
||||
``` shell
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
```
|
||||
|
||||
#### 2.3.2 生成API文档
|
||||
@@ -162,12 +157,30 @@ swag init
|
||||
|
||||
> 执行上面的命令后,server目录下会出现docs文件夹里的 `docs.go`, `swagger.json`, `swagger.yaml` 三个文件更新,启动go服务之后, 在浏览器输入 [http://localhost:8888/swagger/index.html](http://localhost:8888/swagger/index.html) 即可查看swagger文档
|
||||
|
||||
### 2.4 VSCode工作区
|
||||
|
||||
#### 2.4.1 开发
|
||||
|
||||
使用`VSCode`打开根目录下的工作区文件`gin-vue-admin.code-workspace`,在边栏可以看到三个虚拟目录:`backend`、`frontend`、`root`。
|
||||
|
||||
#### 2.4.2 运行/调试
|
||||
|
||||
在运行和调试中也可以看到三个task:`Backend`、`Frontend`、`Both (Backend & Frontend)`。运行`Both (Backend & Frontend)`可以同时启动前后端项目。
|
||||
|
||||
#### 2.4.3 settings
|
||||
|
||||
在工作区配置文件中有`go.toolsEnvVars`字段,是用于`VSCode`自身的go工具环境变量。此外在多go版本的系统中,可以通过`gopath`、`go.goroot`指定运行版本。
|
||||
|
||||
```json
|
||||
"go.gopath": null,
|
||||
"go.goroot": null,
|
||||
```
|
||||
|
||||
## 3. 技术选型
|
||||
|
||||
- 前端:用基于 [Vue](https://vuejs.org) 的 [Element](https://github.com/ElemeFE/element) 构建基础页面。
|
||||
- 后端:用 [Gin](https://gin-gonic.com/) 快速搭建基础restful风格API,[Gin](https://gin-gonic.com/) 是一个go语言编写的Web框架。
|
||||
- 数据库:采用`MySql`(5.6.44)版本,使用 [gorm](http://gorm.cn) 实现对数据库的基本操作。
|
||||
- 数据库:采用`MySql` > (5.7) 版本 数据库引擎 InnoDB,使用 [gorm](http://gorm.cn) 实现对数据库的基本操作。
|
||||
- 缓存:使用`Redis`实现记录当前活跃用户的`jwt`令牌并实现多点登录限制。
|
||||
- API文档:使用`Swagger`构建自动化文档。
|
||||
- 配置文件:使用 [fsnotify](https://github.com/fsnotify/fsnotify) 和 [viper](https://github.com/spf13/viper) 实现`yaml`格式的配置文件。
|
||||
@@ -211,17 +224,76 @@ swag init
|
||||
├── timer (定时器接口封装)
|
||||
└── upload (oss接口封装)
|
||||
|
||||
└─web (前端文件)
|
||||
├─public (发布模板)
|
||||
└─src (源码包)
|
||||
├─api (向后台发送ajax的封装层)
|
||||
├─assets (静态文件)
|
||||
├─components(组件)
|
||||
├─router (前端路由)
|
||||
├─store (vuex 状态管理仓)
|
||||
├─style (通用样式文件)
|
||||
├─utils (前端工具库)
|
||||
└─view (前端页面)
|
||||
web
|
||||
├── babel.config.js
|
||||
├── Dockerfile
|
||||
├── favicon.ico
|
||||
├── index.html -- 主页面
|
||||
├── limit.js -- 助手代码
|
||||
├── package.json -- 包管理器代码
|
||||
├── src -- 源代码
|
||||
│ ├── api -- api 组
|
||||
│ ├── App.vue -- 主页面
|
||||
│ ├── assets -- 静态资源
|
||||
│ ├── components -- 全局组件
|
||||
│ ├── core -- gva 组件包
|
||||
│ │ ├── config.js -- gva网站配置文件
|
||||
│ │ ├── gin-vue-admin.js -- 注册欢迎文件
|
||||
│ │ └── global.js -- 统一导入文件
|
||||
│ ├── directive -- v-auth 注册文件
|
||||
│ ├── main.js -- 主文件
|
||||
│ ├── permission.js -- 路由中间件
|
||||
│ ├── pinia -- pinia 状态管理器,取代vuex
|
||||
│ │ ├── index.js -- 入口文件
|
||||
│ │ └── modules -- modules
|
||||
│ │ ├── dictionary.js
|
||||
│ │ ├── router.js
|
||||
│ │ └── user.js
|
||||
│ ├── router -- 路由声明文件
|
||||
│ │ └── index.js
|
||||
│ ├── style -- 全局样式
|
||||
│ │ ├── base.scss
|
||||
│ │ ├── basics.scss
|
||||
│ │ ├── element_visiable.scss -- 此处可以全局覆盖 element-plus 样式
|
||||
│ │ ├── iconfont.css -- 顶部几个icon的样式文件
|
||||
│ │ ├── main.scss
|
||||
│ │ ├── mobile.scss
|
||||
│ │ └── newLogin.scss
|
||||
│ ├── utils -- 方法包库
|
||||
│ │ ├── asyncRouter.js -- 动态路由相关
|
||||
│ │ ├── btnAuth.js -- 动态权限按钮相关
|
||||
│ │ ├── bus.js -- 全局mitt声明文件
|
||||
│ │ ├── date.js -- 日期相关
|
||||
│ │ ├── dictionary.js -- 获取字典方法
|
||||
│ │ ├── downloadImg.js -- 下载图片方法
|
||||
│ │ ├── format.js -- 格式整理相关
|
||||
│ │ ├── image.js -- 图片相关方法
|
||||
│ │ ├── page.js -- 设置页面标题
|
||||
│ │ ├── request.js -- 请求
|
||||
│ │ └── stringFun.js -- 字符串文件
|
||||
| ├── view -- 主要view代码
|
||||
| | ├── about -- 关于我们
|
||||
| | ├── dashboard -- 面板
|
||||
| | ├── error -- 错误
|
||||
| | ├── example --上传案例
|
||||
| | ├── iconList -- icon列表
|
||||
| | ├── init -- 初始化数据
|
||||
| | | ├── index -- 新版本
|
||||
| | | ├── init -- 旧版本
|
||||
| | ├── layout -- layout约束页面
|
||||
| | | ├── aside
|
||||
| | | ├── bottomInfo -- bottomInfo
|
||||
| | | ├── screenfull -- 全屏设置
|
||||
| | | ├── setting -- 系统设置
|
||||
| | | └── index.vue -- base 约束
|
||||
| | ├── login --登录
|
||||
| | ├── person --个人中心
|
||||
| | ├── superAdmin -- 超级管理员操作
|
||||
| | ├── system -- 系统检测页面
|
||||
| | ├── systemTools -- 系统配置相关页面
|
||||
| | └── routerHolder.vue -- page 入口页面
|
||||
├── vite.config.js -- vite 配置文件
|
||||
└── yarn.lock
|
||||
|
||||
```
|
||||
|
||||
@@ -240,8 +312,8 @@ swag init
|
||||
- 前端文件参考: [web/src/view/superAdmin/api/api.vue](https://github.com/flipped-aurora/gin-vue-admin/blob/master/web/src/view/superAdmin/api/api.vue)
|
||||
- 后台文件参考: [server/router/sys_api.go](https://github.com/flipped-aurora/gin-vue-admin/blob/master/server/router/sys_api.go)
|
||||
- 多点登录限制:需要在`config.yaml`中把`system`中的`use-multipoint`修改为true(需要自行配置Redis和Config中的Redis参数,测试阶段,有bug请及时反馈)。
|
||||
- 分片长传:提供文件分片上传和大文件分片上传功能示例。
|
||||
- 表单生成器:表单生成器借助 [@form-generator](https://github.com/JakHuang/form-generator) 。
|
||||
- 分片上传:提供文件分片上传和大文件分片上传功能示例。
|
||||
- 表单生成器:表单生成器借助 [@Variant Form](https://github.com/vform666/variant-form) 。
|
||||
- 代码生成器:后台基础逻辑以及简单curd的代码生成器。
|
||||
|
||||
## 6. 知识库
|
||||
@@ -272,38 +344,42 @@ swag init
|
||||
|
||||
(5)gin-vue-admin 版本更新介绍视频
|
||||
|
||||
> bilibili:https://space.bilibili.com/322210472/channel/detail?cid=126418&ctype=0
|
||||
> bilibili:https://www.bilibili.com/video/BV1kv4y1g7nT
|
||||
|
||||
## 7. 联系方式
|
||||
|
||||
### 7.1 技术群
|
||||
|
||||
### QQ交流群:622360840
|
||||
| QQ 群 |
|
||||
| :---: |
|
||||
| <img src="http://qmplusimg.henrongyi.top/qq.jpg" width="180"/> |
|
||||
### QQ交流群:971857775
|
||||
|
||||
### 微信交流群
|
||||
| 微信 |
|
||||
| :---: |
|
||||
| <img width="150" src="http://qmplusimg.henrongyi.top/qrjjz.png">
|
||||
|
||||
添加微信,备注"加入gin-vue-admin交流群"
|
||||
防止广告进群,添加微信,输入以下代码执行结果(请勿转码为string)
|
||||
|
||||
### [关于我们](https://www.gin-vue-admin.com/about/)
|
||||
```
|
||||
str := "5Yqg5YWlR1ZB5Lqk5rWB576k"
|
||||
decodeBytes, err := base64.StdEncoding.DecodeString(str)
|
||||
fmt.Println(decodeBytes, err)
|
||||
```
|
||||
|
||||
### [关于我们](https://www.gin-vue-admin.com/about/join.html)
|
||||
|
||||
## 8. 贡献者
|
||||
|
||||
感谢您对gin-vue-admin的贡献!
|
||||
|
||||
<a href="https://github.com/flipped-aurora/gin-vue-admin/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=flipped-aurora/gin-vue-admin" />
|
||||
</a>
|
||||
<a href="https://openomy.app/github/flipped-aurora/gin-vue-admin" target="_blank" style="display: block; width: 100%;" align="center">
|
||||
<img src="https://openomy.app/svg?repo=flipped-aurora/gin-vue-admin&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
|
||||
</a>
|
||||
|
||||
## 9. 捐赠
|
||||
|
||||
如果你觉得这个项目对你有帮助,你可以请作者喝饮料 :tropical_drink: [点我](https://www.gin-vue-admin.com/docs/coffee)
|
||||
如果你觉得这个项目对你有帮助,你可以请作者喝饮料 :tropical_drink: [点我](https://www.gin-vue-admin.com/coffee/index.html)
|
||||
|
||||
## 10. 商用注意事项
|
||||
## 10. 注意事项
|
||||
|
||||
如果您将此项目用于商业用途,请遵守Apache2.0协议并保留作者技术支持声明。
|
||||
请严格遵守Apache 2.0协议并保留作品声明,去除版权信息请务必[获取授权](https://www.gin-vue-admin.com/empower/)
|
||||
未授权去除版权信息将依法追究法律责任
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
version: "3"
|
||||
|
||||
# 声明一个名为network的networks,subnet为network的子网地址,默认网关是177.7.0.1
|
||||
networks:
|
||||
network:
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: '177.7.0.0/16'
|
||||
|
||||
|
||||
# 设置mysql,redis持久化保存
|
||||
volumes:
|
||||
mysql:
|
||||
redis:
|
||||
|
||||
services:
|
||||
web:
|
||||
build:
|
||||
context: ./web
|
||||
context: ../../web
|
||||
dockerfile: ./Dockerfile
|
||||
container_name: gva-web
|
||||
restart: always
|
||||
@@ -25,15 +31,17 @@ services:
|
||||
|
||||
server:
|
||||
build:
|
||||
context: ./server
|
||||
context: ../../server
|
||||
dockerfile: ./Dockerfile
|
||||
container_name: gva-server
|
||||
restart: always
|
||||
ports:
|
||||
- '8888:8888'
|
||||
depends_on:
|
||||
- mysql
|
||||
- redis
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
links:
|
||||
- mysql
|
||||
- redis
|
||||
@@ -42,15 +50,24 @@ services:
|
||||
ipv4_address: 177.7.0.12
|
||||
|
||||
mysql:
|
||||
image: mysql:8.0.21
|
||||
image: mysql:8.0.21 # 如果您是 arm64 架构:如 MacOS 的 M1,请修改镜像为 image: mysql/mysql-server:8.0.21
|
||||
container_name: gva-mysql
|
||||
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci #设置utf8字符集
|
||||
restart: always
|
||||
ports:
|
||||
- "13306:3306" # host物理直接映射端口为13306
|
||||
environment:
|
||||
#MYSQL_ROOT_PASSWORD: 'Aa@6447985' # root管理员用户密码
|
||||
MYSQL_DATABASE: 'qmPlus' # 初始化启动时要创建的数据库的名称
|
||||
MYSQL_ROOT_PASSWORD: 'Aa@6447985' # root管理员用户密码
|
||||
MYSQL_USER: 'gva'
|
||||
MYSQL_PASSWORD: 'Aa@6447985'
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "gva", "-pAa@6447985"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
volumes:
|
||||
- mysql:/var/lib/mysql
|
||||
networks:
|
||||
network:
|
||||
ipv4_address: 177.7.0.13
|
||||
@@ -61,6 +78,13 @@ services:
|
||||
restart: always
|
||||
ports:
|
||||
- '16379:6379'
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "redis-cli ping | grep PONG || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
volumes:
|
||||
- redis:/data
|
||||
networks:
|
||||
network:
|
||||
ipv4_address: 177.7.0.14
|
||||
ipv4_address: 177.7.0.14
|
||||
@@ -0,0 +1,18 @@
|
||||
FROM centos:7
|
||||
WORKDIR /opt
|
||||
ENV LANG=en_US.utf8
|
||||
COPY deploy/docker/entrypoint.sh .
|
||||
COPY build/ /usr/share/nginx/html/
|
||||
COPY server/config.yaml /usr/share/nginx/html/config.yaml
|
||||
COPY web/.docker-compose/nginx/conf.d/nginx.conf /etc/nginx/conf.d/nginx.conf
|
||||
RUN set -ex \
|
||||
&& echo "LANG=en_US.utf8" > /etc/locale.conf \
|
||||
&& echo "net.core.somaxconn = 1024" >> /etc/sysctl.conf \
|
||||
&& echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf \
|
||||
&& yum -y install epel-release \
|
||||
&& yum -y localinstall http://mirrors.ustc.edu.cn/mysql-repo/mysql57-community-release-el7.rpm \
|
||||
&& yum -y install mysql-community-server git redis nginx go npm --nogpgcheck && chmod +x ./entrypoint.sh \
|
||||
&& npm install -g yarn && go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct \
|
||||
&& echo "start" > /dev/null
|
||||
EXPOSE 80
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
if [ ! -d "/var/lib/mysql/gva" ]; then
|
||||
mysqld --initialize-insecure --user=mysql --datadir=/var/lib/mysql
|
||||
mysqld --daemonize --user=mysql
|
||||
sleep 5s
|
||||
mysql -uroot -e "create database gva default charset 'utf8' collate 'utf8_bin'; grant all on gva.* to 'root'@'127.0.0.1' identified by '123456'; flush privileges;"
|
||||
else
|
||||
mysqld --daemonize --user=mysql
|
||||
fi
|
||||
redis-server &
|
||||
if [ "$1" = "actions" ]; then
|
||||
cd /opt/gva/server && go run main.go &
|
||||
cd /opt/gva/web/ && yarn serve &
|
||||
else
|
||||
/usr/sbin/nginx &
|
||||
cd /usr/share/nginx/html/ && ./server &
|
||||
fi
|
||||
echo "gva ALL start!!!"
|
||||
tail -f /dev/null
|
||||
+25
-19
@@ -23,13 +23,13 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: gin-vue-admin-container
|
||||
image: registry.cn-hangzhou.aliyuncs.com/gin-vue-admin/server:40fc3aa6
|
||||
imagePullPolicy: IfNotPresent
|
||||
image: registry.cn-hangzhou.aliyuncs.com/gva/server:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8888
|
||||
name: http
|
||||
volumeMounts:
|
||||
- mountPath: /go/src/github.com/flipped-aurora/gin-vue-admin/server/config.yaml
|
||||
- mountPath: /go/src/github.com/flipped-aurora/gin-vue-admin/server/config.docker.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /etc/localtime
|
||||
@@ -41,22 +41,28 @@ spec:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 200Mi
|
||||
# readinessProbe:
|
||||
# httpGet:
|
||||
# path: /ready
|
||||
# port: http
|
||||
# initialDelaySeconds: 20
|
||||
# periodSeconds: 10
|
||||
# successThreshold: 1
|
||||
# failureThreshold: 3
|
||||
# livenessProbe:
|
||||
# httpGet:
|
||||
# path: /health
|
||||
# port: http
|
||||
# initialDelaySeconds: 20
|
||||
# periodSeconds: 10
|
||||
# successThreshold: 1
|
||||
# failureThreshold: 3
|
||||
livenessProbe:
|
||||
failureThreshold: 1
|
||||
periodSeconds: 5
|
||||
successThreshold: 1
|
||||
tcpSocket:
|
||||
port: 8888
|
||||
timeoutSeconds: 1
|
||||
readinessProbe:
|
||||
failureThreshold: 3
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 5
|
||||
successThreshold: 1
|
||||
tcpSocket:
|
||||
port: 8888
|
||||
timeoutSeconds: 1
|
||||
startupProbe:
|
||||
failureThreshold: 40
|
||||
periodSeconds: 5
|
||||
successThreshold: 1
|
||||
tcpSocket:
|
||||
port: 8888
|
||||
timeoutSeconds: 1
|
||||
#imagePullSecrets:
|
||||
# - name: docker-registry
|
||||
volumes:
|
||||
+2
-2
@@ -23,8 +23,8 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: gin-vue-admin-nginx-container
|
||||
image: registry.cn-hangzhou.aliyuncs.com/gin-vue-admin/web:40fc3aa6
|
||||
imagePullPolicy: IfNotPresent
|
||||
image: registry.cn-hangzhou.aliyuncs.com/gva/web:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
@@ -0,0 +1,18 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: gva-ingress
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx"
|
||||
spec:
|
||||
rules:
|
||||
- host: demo.gin-vue-admin.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: gva-web
|
||||
port:
|
||||
number: 8080
|
||||
@@ -10,8 +10,8 @@ metadata:
|
||||
app: gva-web
|
||||
version: gva-vue3
|
||||
spec:
|
||||
type: NodePort
|
||||
# type: ClusterIP
|
||||
# type: NodePort
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "server",
|
||||
"name": "backend"
|
||||
},
|
||||
{
|
||||
"path": "web",
|
||||
"name": "frontend"
|
||||
},
|
||||
{
|
||||
"path": ".",
|
||||
"name": "root"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"go.toolsEnvVars": {
|
||||
"GOPROXY": "https://goproxy.cn,direct",
|
||||
"GONOPROXY": "none;"
|
||||
}
|
||||
},
|
||||
"launch": {
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"name": "Backend",
|
||||
"cwd": "${workspaceFolder:backend}",
|
||||
"program": "${workspaceFolder:backend}/"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder:frontend}",
|
||||
"name": "Frontend",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": ["run-script", "serve"]
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Both (Backend & Frontend)",
|
||||
"configurations": ["Backend", "Frontend"],
|
||||
"stopAll": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+17
-8
@@ -3,20 +3,29 @@ FROM golang:alpine as builder
|
||||
WORKDIR /go/src/github.com/flipped-aurora/gin-vue-admin/server
|
||||
COPY . .
|
||||
|
||||
RUN go env -w GO111MODULE=on
|
||||
RUN go env -w GOPROXY=https://goproxy.cn,direct
|
||||
RUN go env -w CGO_ENABLED=0
|
||||
RUN go env
|
||||
RUN go mod tidy
|
||||
RUN go build -o server .
|
||||
RUN go env -w GO111MODULE=on \
|
||||
&& go env -w GOPROXY=https://goproxy.cn,direct \
|
||||
&& go env -w CGO_ENABLED=0 \
|
||||
&& go env \
|
||||
&& go mod tidy \
|
||||
&& go build -o server .
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
LABEL MAINTAINER="SliverHorn@sliver_horn@qq.com"
|
||||
# 设置时区
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN apk update && apk add --no-cache tzdata openntpd \
|
||||
&& ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
WORKDIR /go/src/github.com/flipped-aurora/gin-vue-admin/server
|
||||
|
||||
COPY --from=0 /go/src/github.com/flipped-aurora/gin-vue-admin/server ./
|
||||
COPY --from=0 /go/src/github.com/flipped-aurora/gin-vue-admin/server/server ./
|
||||
COPY --from=0 /go/src/github.com/flipped-aurora/gin-vue-admin/server/resource ./resource/
|
||||
COPY --from=0 /go/src/github.com/flipped-aurora/gin-vue-admin/server/config.docker.yaml ./
|
||||
|
||||
# 挂载目录:如果使用了sqlite数据库,容器命令示例:docker run -d -v /宿主机路径/gva.db:/go/src/github.com/flipped-aurora/gin-vue-admin/server/gva.db -p 8888:8888 --name gva-server-v1 gva-server:1.0
|
||||
# VOLUME ["/go/src/github.com/flipped-aurora/gin-vue-admin/server"]
|
||||
|
||||
EXPOSE 8888
|
||||
|
||||
ENTRYPOINT ./server -c config.docker.yaml
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
package autocode
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/autocode"
|
||||
autocodeReq "github.com/flipped-aurora/gin-vue-admin/server/model/autocode/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeExampleApi struct{}
|
||||
|
||||
var autoCodeExampleService = service.ServiceGroupApp.AutoCodeServiceGroup.AutoCodeExampleService
|
||||
|
||||
// @Tags AutoCodeExample
|
||||
// @Summary 创建AutoCodeExample
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body autocode.AutoCodeExample true "AutoCodeExample模型"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCodeExample/createAutoCodeExample [post]
|
||||
func (autoCodeExampleApi *AutoCodeExampleApi) CreateAutoCodeExample(c *gin.Context) {
|
||||
var autoCodeExample autocode.AutoCodeExample
|
||||
_ = c.ShouldBindJSON(&autoCodeExample)
|
||||
if err := autoCodeExampleService.CreateAutoCodeExample(autoCodeExample); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags AutoCodeExample
|
||||
// @Summary 删除AutoCodeExample
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body autocode.AutoCodeExample true "AutoCodeExample模型"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /autoCodeExample/deleteAutoCodeExample [delete]
|
||||
func (autoCodeExampleApi *AutoCodeExampleApi) DeleteAutoCodeExample(c *gin.Context) {
|
||||
var autoCodeExample autocode.AutoCodeExample
|
||||
_ = c.ShouldBindJSON(&autoCodeExample)
|
||||
if err := autoCodeExampleService.DeleteAutoCodeExample(autoCodeExample); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags AutoCodeExample
|
||||
// @Summary 更新AutoCodeExample
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body autocode.AutoCodeExample true "更新AutoCodeExample"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /autoCodeExample/updateAutoCodeExample [put]
|
||||
func (autoCodeExampleApi *AutoCodeExampleApi) UpdateAutoCodeExample(c *gin.Context) {
|
||||
var autoCodeExample autocode.AutoCodeExample
|
||||
_ = c.ShouldBindJSON(&autoCodeExample)
|
||||
if err := autoCodeExampleService.UpdateAutoCodeExample(&autoCodeExample); err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags AutoCodeExample
|
||||
// @Summary 用id查询AutoCodeExample
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query autocode.AutoCodeExample true "用id查询AutoCodeExample"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /autoCodeExample/findAutoCodeExample [get]
|
||||
func (autoCodeExampleApi *AutoCodeExampleApi) FindAutoCodeExample(c *gin.Context) {
|
||||
var autoCodeExample autocode.AutoCodeExample
|
||||
_ = c.ShouldBindQuery(&autoCodeExample)
|
||||
if err := utils.Verify(autoCodeExample, utils.IdVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, reAutoCodeExample := autoCodeExampleService.GetAutoCodeExample(autoCodeExample.ID); err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"reAutoCodeExample": reAutoCodeExample}, "查询成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags AutoCodeExample
|
||||
// @Summary 分页获取AutoCodeExample列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query autocodeReq.AutoCodeExampleSearch true "页码, 每页大小, 搜索条件"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /autoCodeExample/getAutoCodeExampleList [get]
|
||||
func (autoCodeExampleApi *AutoCodeExampleApi) GetAutoCodeExampleList(c *gin.Context) {
|
||||
var pageInfo autocodeReq.AutoCodeExampleSearch
|
||||
_ = c.ShouldBindQuery(&pageInfo)
|
||||
if err, list, total := autoCodeExampleService.GetAutoCodeExampleInfoList(pageInfo); err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package autocode
|
||||
|
||||
type ApiGroup struct {
|
||||
// Code generated by github.com/flipped-aurora/gin-vue-admin/server Begin; DO NOT EDIT.
|
||||
AutoCodeExampleApi
|
||||
// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/autocode"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/example"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/system"
|
||||
)
|
||||
|
||||
type ApiGroup struct {
|
||||
SystemApiGroup system.ApiGroup
|
||||
ExampleApiGroup example.ApiGroup
|
||||
AutoCodeApiGroup autocode.ApiGroup
|
||||
}
|
||||
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
|
||||
type ApiGroup struct {
|
||||
SystemApiGroup system.ApiGroup
|
||||
ExampleApiGroup example.ApiGroup
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ package example
|
||||
import "github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
|
||||
type ApiGroup struct {
|
||||
ExcelApi
|
||||
CustomerApi
|
||||
FileUploadAndDownloadApi
|
||||
AttachmentCategoryApi
|
||||
}
|
||||
|
||||
var (
|
||||
excelService = service.ServiceGroupApp.ExampleServiceGroup.ExcelService
|
||||
customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
|
||||
fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
|
||||
attachmentCategoryService = service.ServiceGroupApp.ExampleServiceGroup.AttachmentCategoryService
|
||||
)
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AttachmentCategoryApi struct{}
|
||||
|
||||
// GetCategoryList
|
||||
// @Tags GetCategoryList
|
||||
// @Summary 媒体库分类列表
|
||||
// @Security AttachmentCategory
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=example.ExaAttachmentCategory,msg=string} "媒体库分类列表"
|
||||
// @Router /attachmentCategory/getCategoryList [get]
|
||||
func (a *AttachmentCategoryApi) GetCategoryList(c *gin.Context) {
|
||||
res, err := attachmentCategoryService.GetCategoryList()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取分类列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取分类列表失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(res, c)
|
||||
}
|
||||
|
||||
// AddCategory
|
||||
// @Tags AddCategory
|
||||
// @Summary 添加媒体库分类
|
||||
// @Security AttachmentCategory
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaAttachmentCategory true "媒体库分类数据"// @Success 200 {object} response.Response{msg=string} "添加媒体库分类"
|
||||
// @Router /attachmentCategory/addCategory [post]
|
||||
func (a *AttachmentCategoryApi) AddCategory(c *gin.Context) {
|
||||
var req example.ExaAttachmentCategory
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
global.GVA_LOG.Error("参数错误!", zap.Error(err))
|
||||
response.FailWithMessage("参数错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := attachmentCategoryService.AddCategory(&req); err != nil {
|
||||
global.GVA_LOG.Error("创建/更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建/更新失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建/更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteCategory
|
||||
// @Tags DeleteCategory
|
||||
// @Summary 删除分类
|
||||
// @Security AttachmentCategory
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body common.GetById true "分类id"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除分类"
|
||||
// @Router /attachmentCategory/deleteCategory [post]
|
||||
func (a *AttachmentCategoryApi) DeleteCategory(c *gin.Context) {
|
||||
var req common.GetById
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage("参数错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.ID == 0 {
|
||||
response.FailWithMessage("参数错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := attachmentCategoryService.DeleteCategory(&req.ID); err != nil {
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
@@ -2,10 +2,12 @@ package example
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response"
|
||||
@@ -14,15 +16,16 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 断点续传到服务器
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "an example for breakpoint resume, 断点续传示例"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"切片创建成功"}"
|
||||
// @Router /fileUploadAndDownload/breakpointContinue [post]
|
||||
func (u *FileUploadAndDownloadApi) BreakpointContinue(c *gin.Context) {
|
||||
// BreakpointContinue
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 断点续传到服务器
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "an example for breakpoint resume, 断点续传示例"
|
||||
// @Success 200 {object} response.Response{msg=string} "断点续传到服务器"
|
||||
// @Router /fileUploadAndDownload/breakpointContinue [post]
|
||||
func (b *FileUploadAndDownloadApi) BreakpointContinue(c *gin.Context) {
|
||||
fileMd5 := c.Request.FormValue("fileMd5")
|
||||
fileName := c.Request.FormValue("fileName")
|
||||
chunkMd5 := c.Request.FormValue("chunkMd5")
|
||||
@@ -46,26 +49,26 @@ func (u *FileUploadAndDownloadApi) BreakpointContinue(c *gin.Context) {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}(f)
|
||||
cen, _ := ioutil.ReadAll(f)
|
||||
cen, _ := io.ReadAll(f)
|
||||
if !utils.CheckMd5(cen, chunkMd5) {
|
||||
global.GVA_LOG.Error("检查md5失败!", zap.Error(err))
|
||||
response.FailWithMessage("检查md5失败", c)
|
||||
return
|
||||
}
|
||||
err, file := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal)
|
||||
file, err := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查找或创建记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("查找或创建记录失败", c)
|
||||
return
|
||||
}
|
||||
err, pathc := utils.BreakPointContinue(cen, fileName, chunkNumber, chunkTotal, fileMd5)
|
||||
pathC, err := utils.BreakPointContinue(cen, fileName, chunkNumber, chunkTotal, fileMd5)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("断点续传失败!", zap.Error(err))
|
||||
response.FailWithMessage("断点续传失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
if err = fileUploadAndDownloadService.CreateFileChunk(file.ID, pathc, chunkNumber); err != nil {
|
||||
if err = fileUploadAndDownloadService.CreateFileChunk(file.ID, pathC, chunkNumber); err != nil {
|
||||
global.GVA_LOG.Error("创建文件记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建文件记录失败", c)
|
||||
return
|
||||
@@ -73,19 +76,20 @@ func (u *FileUploadAndDownloadApi) BreakpointContinue(c *gin.Context) {
|
||||
response.OkWithMessage("切片创建成功", c)
|
||||
}
|
||||
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 查找文件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "Find the file, 查找文件"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查找成功"}"
|
||||
// @Router /fileUploadAndDownload/findFile [post]
|
||||
func (u *FileUploadAndDownloadApi) FindFile(c *gin.Context) {
|
||||
// FindFile
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 查找文件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "Find the file, 查找文件"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.FileResponse,msg=string} "查找文件,返回包括文件详情"
|
||||
// @Router /fileUploadAndDownload/findFile [get]
|
||||
func (b *FileUploadAndDownloadApi) FindFile(c *gin.Context) {
|
||||
fileMd5 := c.Query("fileMd5")
|
||||
fileName := c.Query("fileName")
|
||||
chunkTotal, _ := strconv.Atoi(c.Query("chunkTotal"))
|
||||
err, file := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal)
|
||||
file, err := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查找失败!", zap.Error(err))
|
||||
response.FailWithMessage("查找失败", c)
|
||||
@@ -94,18 +98,19 @@ func (u *FileUploadAndDownloadApi) FindFile(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 创建文件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "上传文件完成"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"file uploaded, 文件创建成功"}"
|
||||
// @Router /fileUploadAndDownload/findFile [post]
|
||||
// BreakpointContinueFinish
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 创建文件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "上传文件完成"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.FilePathResponse,msg=string} "创建文件,返回包括文件路径"
|
||||
// @Router /fileUploadAndDownload/findFile [post]
|
||||
func (b *FileUploadAndDownloadApi) BreakpointContinueFinish(c *gin.Context) {
|
||||
fileMd5 := c.Query("fileMd5")
|
||||
fileName := c.Query("fileName")
|
||||
err, filePath := utils.MakeFile(fileName, fileMd5)
|
||||
filePath, err := utils.MakeFile(fileName, fileMd5)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("文件创建失败!", zap.Error(err))
|
||||
response.FailWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建失败", c)
|
||||
@@ -114,27 +119,32 @@ func (b *FileUploadAndDownloadApi) BreakpointContinueFinish(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 删除切片
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "删除缓存切片"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"缓存切片删除成功"}"
|
||||
// @Router /fileUploadAndDownload/removeChunk [post]
|
||||
func (u *FileUploadAndDownloadApi) RemoveChunk(c *gin.Context) {
|
||||
fileMd5 := c.Query("fileMd5")
|
||||
fileName := c.Query("fileName")
|
||||
filePath := c.Query("filePath")
|
||||
err := utils.RemoveChunk(fileMd5)
|
||||
// RemoveChunk
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 删除切片
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "删除缓存切片"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除切片"
|
||||
// @Router /fileUploadAndDownload/removeChunk [post]
|
||||
func (b *FileUploadAndDownloadApi) RemoveChunk(c *gin.Context) {
|
||||
var file example.ExaFile
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = fileUploadAndDownloadService.DeleteFileChunk(fileMd5, fileName, filePath)
|
||||
err = utils.RemoveChunk(file.FileMd5)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("缓存切片删除失败!", zap.Error(err))
|
||||
response.FailWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "缓存切片删除失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "缓存切片删除成功", c)
|
||||
return
|
||||
}
|
||||
err = fileUploadAndDownloadService.DeleteFileChunk(file.FileMd5, file.FilePath)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error(err.Error(), zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("缓存切片删除成功", c)
|
||||
}
|
||||
|
||||
@@ -13,130 +13,164 @@ import (
|
||||
|
||||
type CustomerApi struct{}
|
||||
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 创建客户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户用户名, 客户手机号码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /customer/customer [post]
|
||||
// CreateExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 创建客户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户用户名, 客户手机号码"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建客户"
|
||||
// @Router /customer/customer [post]
|
||||
func (e *CustomerApi) CreateExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
_ = c.ShouldBindJSON(&customer)
|
||||
if err := utils.Verify(customer, utils.CustomerVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer, utils.CustomerVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
customer.SysUserID = utils.GetUserID(c)
|
||||
customer.SysUserAuthorityID = utils.GetUserAuthorityId(c)
|
||||
if err := customerService.CreateExaCustomer(customer); err != nil {
|
||||
err = customerService.CreateExaCustomer(customer)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 删除客户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /customer/customer [delete]
|
||||
// DeleteExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 删除客户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除客户"
|
||||
// @Router /customer/customer [delete]
|
||||
func (e *CustomerApi) DeleteExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
_ = c.ShouldBindJSON(&customer)
|
||||
if err := utils.Verify(customer.GVA_MODEL, utils.IdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := customerService.DeleteExaCustomer(customer); err != nil {
|
||||
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = customerService.DeleteExaCustomer(customer)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 更新客户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户ID, 客户信息"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /customer/customer [put]
|
||||
// UpdateExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 更新客户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户ID, 客户信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新客户信息"
|
||||
// @Router /customer/customer [put]
|
||||
func (e *CustomerApi) UpdateExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
_ = c.ShouldBindJSON(&customer)
|
||||
if err := utils.Verify(customer.GVA_MODEL, utils.IdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := utils.Verify(customer, utils.CustomerVerify); err != nil {
|
||||
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := customerService.UpdateExaCustomer(&customer); err != nil {
|
||||
err = utils.Verify(customer, utils.CustomerVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = customerService.UpdateExaCustomer(&customer)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 获取单一客户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query example.ExaCustomer true "客户ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /customer/customer [get]
|
||||
// GetExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 获取单一客户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query example.ExaCustomer true "客户ID"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.ExaCustomerResponse,msg=string} "获取单一客户信息,返回包括客户详情"
|
||||
// @Router /customer/customer [get]
|
||||
func (e *CustomerApi) GetExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
_ = c.ShouldBindQuery(&customer)
|
||||
if err := utils.Verify(customer.GVA_MODEL, utils.IdVerify); err != nil {
|
||||
err := c.ShouldBindQuery(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err, data := customerService.GetExaCustomer(customer.ID)
|
||||
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
data, err := customerService.GetExaCustomer(customer.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(exampleRes.ExaCustomerResponse{Customer: data}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(exampleRes.ExaCustomerResponse{Customer: data}, "获取成功", c)
|
||||
}
|
||||
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 分页获取权限客户列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /customer/customerList [get]
|
||||
// GetExaCustomerList
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 分页获取权限客户列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取权限客户列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /customer/customerList [get]
|
||||
func (e *CustomerApi) GetExaCustomerList(c *gin.Context) {
|
||||
var pageInfo request.PageInfo
|
||||
_ = c.ShouldBindQuery(&pageInfo)
|
||||
if err := utils.Verify(pageInfo, utils.PageInfoVerify); err != nil {
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err, customerList, total := customerService.GetCustomerInfoList(utils.GetUserAuthorityId(c), pageInfo)
|
||||
err = utils.Verify(pageInfo, utils.PageInfoVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
customerList, total, err := customerService.GetCustomerInfoList(utils.GetUserAuthorityId(c), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败"+err.Error(), c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: customerList,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: customerList,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ExcelApi struct{}
|
||||
|
||||
// /excel/importExcel 接口,与upload接口作用类似,只是把文件存到resource/excel目录下,用于导入Excel时存放Excel文件(ExcelImport.xlsx)
|
||||
// /excel/loadExcel接口,用于读取resource/excel目录下的文件((ExcelImport.xlsx)并加载为[]model.SysBaseMenu类型的示例数据
|
||||
// /excel/exportExcel 接口,用于读取前端传来的tableData,生成Excel文件并返回
|
||||
// /excel/downloadTemplate 接口,用于下载resource/excel目录下的 ExcelTemplate.xlsx 文件,作为导入的模板
|
||||
|
||||
// @Tags excel
|
||||
// @Summary 导出Excel
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/octet-stream
|
||||
// @Param data body example.ExcelInfo true "导出Excel文件信息"
|
||||
// @Success 200
|
||||
// @Router /excel/exportExcel [post]
|
||||
func (e *ExcelApi) ExportExcel(c *gin.Context) {
|
||||
var excelInfo example.ExcelInfo
|
||||
_ = c.ShouldBindJSON(&excelInfo)
|
||||
filePath := global.GVA_CONFIG.Excel.Dir + excelInfo.FileName
|
||||
err := excelService.ParseInfoList2Excel(excelInfo.InfoList, filePath)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("转换Excel失败!", zap.Error(err))
|
||||
response.FailWithMessage("转换Excel失败", c)
|
||||
return
|
||||
}
|
||||
c.Writer.Header().Add("success", "true")
|
||||
c.File(filePath)
|
||||
}
|
||||
|
||||
// @Tags excel
|
||||
// @Summary 导入Excel文件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "导入Excel文件"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"导入成功"}"
|
||||
// @Router /excel/importExcel [post]
|
||||
func (e *ExcelApi) ImportExcel(c *gin.Context) {
|
||||
_, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
|
||||
response.FailWithMessage("接收文件失败", c)
|
||||
return
|
||||
}
|
||||
_ = c.SaveUploadedFile(header, global.GVA_CONFIG.Excel.Dir+"ExcelImport.xlsx")
|
||||
response.OkWithMessage("导入成功", c)
|
||||
}
|
||||
|
||||
// @Tags excel
|
||||
// @Summary 加载Excel数据
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"加载数据成功"}"
|
||||
// @Router /excel/loadExcel [get]
|
||||
func (e *ExcelApi) LoadExcel(c *gin.Context) {
|
||||
menus, err := excelService.ParseExcel2InfoList()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("加载数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("加载数据失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: menus,
|
||||
Total: int64(len(menus)),
|
||||
Page: 1,
|
||||
PageSize: 999,
|
||||
}, "加载数据成功", c)
|
||||
}
|
||||
|
||||
// @Tags excel
|
||||
// @Summary 下载模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param fileName query string true "模板名称"
|
||||
// @Success 200
|
||||
// @Router /excel/downloadTemplate [get]
|
||||
func (e *ExcelApi) DownloadTemplate(c *gin.Context) {
|
||||
fileName := c.Query("fileName")
|
||||
filePath := global.GVA_CONFIG.Excel.Dir + fileName
|
||||
ok, err := utils.PathExists(filePath)
|
||||
if !ok || err != nil {
|
||||
global.GVA_LOG.Error("文件不存在!", zap.Error(err))
|
||||
response.FailWithMessage("文件不存在", c)
|
||||
return
|
||||
}
|
||||
c.Writer.Header().Add("success", "true")
|
||||
c.File(filePath)
|
||||
}
|
||||
@@ -2,52 +2,77 @@ package example
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/example/request"
|
||||
exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type FileUploadAndDownloadApi struct{}
|
||||
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 上传文件示例
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "上传文件示例"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"上传成功"}"
|
||||
// @Router /fileUploadAndDownload/upload [post]
|
||||
func (u *FileUploadAndDownloadApi) UploadFile(c *gin.Context) {
|
||||
// UploadFile
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 上传文件示例
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "上传文件示例"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.ExaFileResponse,msg=string} "上传文件示例,返回包括文件详情"
|
||||
// @Router /fileUploadAndDownload/upload [post]
|
||||
func (b *FileUploadAndDownloadApi) UploadFile(c *gin.Context) {
|
||||
var file example.ExaFileUploadAndDownload
|
||||
noSave := c.DefaultQuery("noSave", "0")
|
||||
_, header, err := c.Request.FormFile("file")
|
||||
classId, _ := strconv.Atoi(c.DefaultPostForm("classId", "0"))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
|
||||
response.FailWithMessage("接收文件失败", c)
|
||||
return
|
||||
}
|
||||
err, file = fileUploadAndDownloadService.UploadFile(header, noSave) // 文件上传后拿到文件路径
|
||||
file, err = fileUploadAndDownloadService.UploadFile(header, noSave, classId) // 文件上传后拿到文件路径
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改数据库链接失败!", zap.Error(err))
|
||||
response.FailWithMessage("修改数据库链接失败", c)
|
||||
global.GVA_LOG.Error("上传文件失败!", zap.Error(err))
|
||||
response.FailWithMessage("上传文件失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(exampleRes.ExaFileResponse{File: file}, "上传成功", c)
|
||||
}
|
||||
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 删除文件
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaFileUploadAndDownload true "传入文件里面id即可"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /fileUploadAndDownload/deleteFile [post]
|
||||
func (u *FileUploadAndDownloadApi) DeleteFile(c *gin.Context) {
|
||||
// EditFileName 编辑文件名或者备注
|
||||
func (b *FileUploadAndDownloadApi) EditFileName(c *gin.Context) {
|
||||
var file example.ExaFileUploadAndDownload
|
||||
_ = c.ShouldBindJSON(&file)
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = fileUploadAndDownloadService.EditFileName(file)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("编辑失败!", zap.Error(err))
|
||||
response.FailWithMessage("编辑失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("编辑成功", c)
|
||||
}
|
||||
|
||||
// DeleteFile
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 删除文件
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaFileUploadAndDownload true "传入文件里面id即可"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除文件"
|
||||
// @Router /fileUploadAndDownload/deleteFile [post]
|
||||
func (b *FileUploadAndDownloadApi) DeleteFile(c *gin.Context) {
|
||||
var file example.ExaFileUploadAndDownload
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := fileUploadAndDownloadService.DeleteFile(file); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
@@ -56,27 +81,55 @@ func (u *FileUploadAndDownloadApi) DeleteFile(c *gin.Context) {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 分页文件列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /fileUploadAndDownload/getFileList [post]
|
||||
func (u *FileUploadAndDownloadApi) GetFileList(c *gin.Context) {
|
||||
var pageInfo request.PageInfo
|
||||
_ = c.ShouldBindJSON(&pageInfo)
|
||||
err, list, total := fileUploadAndDownloadService.GetFileRecordInfoList(pageInfo)
|
||||
// GetFileList
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 分页文件列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ExaAttachmentCategorySearch true "页码, 每页大小, 分类id"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页文件列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /fileUploadAndDownload/getFileList [post]
|
||||
func (b *FileUploadAndDownloadApi) GetFileList(c *gin.Context) {
|
||||
var pageInfo request.ExaAttachmentCategorySearch
|
||||
err := c.ShouldBindJSON(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := fileUploadAndDownloadService.GetFileRecordInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// ImportURL
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 导入URL
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaFileUploadAndDownload true "对象"
|
||||
// @Success 200 {object} response.Response{msg=string} "导入URL"
|
||||
// @Router /fileUploadAndDownload/importURL [post]
|
||||
func (b *FileUploadAndDownloadApi) ImportURL(c *gin.Context) {
|
||||
var file []example.ExaFileUploadAndDownload
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := fileUploadAndDownloadService.ImportURL(&file); err != nil {
|
||||
global.GVA_LOG.Error("导入URL失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入URL失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("导入URL成功", c)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
request "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeHistoryApi struct{}
|
||||
|
||||
// First
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取meta信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "请求参数"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取meta信息"
|
||||
// @Router /autoCode/getMeta [post]
|
||||
func (a *AutoCodeHistoryApi) First(c *gin.Context) {
|
||||
var info common.GetById
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
data, err := autoCodeHistoryService.First(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"meta": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
// Delete
|
||||
// @Tags AutoCode
|
||||
// @Summary 删除回滚记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "请求参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除回滚记录"
|
||||
// @Router /autoCode/delSysHistory [post]
|
||||
func (a *AutoCodeHistoryApi) Delete(c *gin.Context) {
|
||||
var info common.GetById
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodeHistoryService.Delete(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// RollBack
|
||||
// @Tags AutoCode
|
||||
// @Summary 回滚自动生成代码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.SysAutoHistoryRollBack true "请求参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "回滚自动生成代码"
|
||||
// @Router /autoCode/rollback [post]
|
||||
func (a *AutoCodeHistoryApi) RollBack(c *gin.Context) {
|
||||
var info request.SysAutoHistoryRollBack
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodeHistoryService.RollBack(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("回滚成功", c)
|
||||
}
|
||||
|
||||
// GetList
|
||||
// @Tags AutoCode
|
||||
// @Summary 查询回滚记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body common.PageInfo true "请求参数"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "查询回滚记录,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /autoCode/getSysHistory [post]
|
||||
func (a *AutoCodeHistoryApi) GetList(c *gin.Context) {
|
||||
var info common.PageInfo
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := autoCodeHistoryService.GetList(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: info.Page,
|
||||
PageSize: info.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/mcp/client"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
// Create
|
||||
// @Tags mcp
|
||||
// @Summary 自动McpTool
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoMcpTool true "创建自动代码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/mcp [post]
|
||||
func (a *AutoCodeTemplateApi) MCP(c *gin.Context) {
|
||||
var info request.AutoMcpTool
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
toolFilePath, err := autoCodeTemplateService.CreateMcp(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
response.FailWithMessage("创建失败", c)
|
||||
global.GVA_LOG.Error(err.Error())
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功,MCP Tool路径:"+toolFilePath, c)
|
||||
}
|
||||
|
||||
// Create
|
||||
// @Tags mcp
|
||||
// @Summary 自动McpTool
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoMcpTool true "创建自动代码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/mcpList [post]
|
||||
func (a *AutoCodeTemplateApi) MCPList(c *gin.Context) {
|
||||
|
||||
baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath)
|
||||
|
||||
testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name)
|
||||
defer testClient.Close()
|
||||
toolsRequest := mcp.ListToolsRequest{}
|
||||
|
||||
list, err := testClient.ListTools(c.Request.Context(), toolsRequest)
|
||||
|
||||
if err != nil {
|
||||
response.FailWithMessage("创建失败", c)
|
||||
global.GVA_LOG.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
mcpServerConfig := map[string]interface{}{
|
||||
"mcpServers": map[string]interface{}{
|
||||
global.GVA_CONFIG.MCP.Name: map[string]string{
|
||||
"url": baseUrl,
|
||||
},
|
||||
},
|
||||
}
|
||||
response.OkWithData(gin.H{
|
||||
"mcpServerConfig": mcpServerConfig,
|
||||
"list": list,
|
||||
}, c)
|
||||
}
|
||||
|
||||
// Create
|
||||
// @Tags mcp
|
||||
// @Summary 测试McpTool
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body object true "调用MCP Tool的参数"
|
||||
// @Success 200 {object} response.Response "{"success":true,"data":{},"msg":"测试成功"}"
|
||||
// @Router /autoCode/mcpTest [post]
|
||||
func (a *AutoCodeTemplateApi) MCPTest(c *gin.Context) {
|
||||
// 定义接口请求结构
|
||||
var testRequest struct {
|
||||
Name string `json:"name" binding:"required"` // 工具名称
|
||||
Arguments map[string]interface{} `json:"arguments" binding:"required"` // 工具参数
|
||||
}
|
||||
|
||||
// 绑定JSON请求体
|
||||
if err := c.ShouldBindJSON(&testRequest); err != nil {
|
||||
response.FailWithMessage("参数解析失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建MCP客户端
|
||||
baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath)
|
||||
testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name)
|
||||
if err != nil {
|
||||
response.FailWithMessage("创建MCP客户端失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
defer testClient.Close()
|
||||
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 初始化MCP连接
|
||||
initRequest := mcp.InitializeRequest{}
|
||||
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
|
||||
initRequest.Params.ClientInfo = mcp.Implementation{
|
||||
Name: "testClient",
|
||||
Version: "v1.0.0",
|
||||
}
|
||||
|
||||
_, err = testClient.Initialize(ctx, initRequest)
|
||||
if err != nil {
|
||||
response.FailWithMessage("初始化MCP连接失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 构建工具调用请求
|
||||
request := mcp.CallToolRequest{}
|
||||
request.Params.Name = testRequest.Name
|
||||
request.Params.Arguments = testRequest.Arguments
|
||||
|
||||
// 调用工具
|
||||
result, err := testClient.CallTool(ctx, request)
|
||||
if err != nil {
|
||||
response.FailWithMessage("工具调用失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 处理响应结果
|
||||
if len(result.Content) == 0 {
|
||||
response.FailWithMessage("工具未返回任何内容", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
response.OkWithData(result.Content, c)
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AutoCodePackageApi struct{}
|
||||
|
||||
// Create
|
||||
// @Tags AutoCodePackage
|
||||
// @Summary 创建package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.SysAutoCodePackageCreate true "创建package"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
|
||||
// @Router /autoCode/createPackage [post]
|
||||
func (a *AutoCodePackageApi) Create(c *gin.Context) {
|
||||
var info request.SysAutoCodePackageCreate
|
||||
_ = c.ShouldBindJSON(&info)
|
||||
if err := utils.Verify(info, utils.AutoPackageVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if strings.Contains(info.PackageName, "\\") || strings.Contains(info.PackageName, "/") || strings.Contains(info.PackageName, "..") {
|
||||
response.FailWithMessage("包名不合法", c)
|
||||
return
|
||||
} // PackageName可能导致路径穿越的问题 / 和 \ 都要防止
|
||||
err := autoCodePackageService.Create(c.Request.Context(), &info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// Delete
|
||||
// @Tags AutoCode
|
||||
// @Summary 删除package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body common.GetById true "创建package"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "删除package成功"
|
||||
// @Router /autoCode/delPackage [post]
|
||||
func (a *AutoCodePackageApi) Delete(c *gin.Context) {
|
||||
var info common.GetById
|
||||
_ = c.ShouldBindJSON(&info)
|
||||
err := autoCodePackageService.Delete(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// All
|
||||
// @Tags AutoCodePackage
|
||||
// @Summary 获取package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
|
||||
// @Router /autoCode/getPackage [post]
|
||||
func (a *AutoCodePackageApi) All(c *gin.Context) {
|
||||
data, err := autoCodePackageService.All(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"pkgs": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
// Templates
|
||||
// @Tags AutoCodePackage
|
||||
// @Summary 获取package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
|
||||
// @Router /autoCode/getTemplates [get]
|
||||
func (a *AutoCodePackageApi) Templates(c *gin.Context) {
|
||||
data, err := autoCodePackageService.Templates(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(data, "获取成功", c)
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodePluginApi struct{}
|
||||
|
||||
// Install
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 安装插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param plug formData file true "this is a test file"
|
||||
// @Success 200 {object} response.Response{data=[]interface{},msg=string} "安装插件成功"
|
||||
// @Router /autoCode/installPlugin [post]
|
||||
func (a *AutoCodePluginApi) Install(c *gin.Context) {
|
||||
header, err := c.FormFile("plug")
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
web, server, err := autoCodePluginService.Install(header)
|
||||
webStr := "web插件安装成功"
|
||||
serverStr := "server插件安装成功"
|
||||
if web == -1 {
|
||||
webStr = "web端插件未成功安装,请按照文档自行解压安装,如果为纯后端插件请忽略此条提示"
|
||||
}
|
||||
if server == -1 {
|
||||
serverStr = "server端插件未成功安装,请按照文档自行解压安装,如果为纯前端插件请忽略此条提示"
|
||||
}
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData([]interface{}{
|
||||
gin.H{
|
||||
"code": web,
|
||||
"msg": webStr,
|
||||
},
|
||||
gin.H{
|
||||
"code": server,
|
||||
"msg": serverStr,
|
||||
}}, c)
|
||||
}
|
||||
|
||||
// Packaged
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param plugName query string true "插件名称"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/pubPlug [post]
|
||||
func (a *AutoCodePluginApi) Packaged(c *gin.Context) {
|
||||
plugName := c.Query("plugName")
|
||||
zipPath, err := autoCodePluginService.PubPlug(plugName)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("打包失败!", zap.Error(err))
|
||||
response.FailWithMessage("打包失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage(fmt.Sprintf("打包成功,文件路径为:%s", zipPath), c)
|
||||
}
|
||||
|
||||
// InitMenu
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/initMenu [post]
|
||||
func (a *AutoCodePluginApi) InitMenu(c *gin.Context) {
|
||||
var menuInfo request.InitMenu
|
||||
err := c.ShouldBindJSON(&menuInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodePluginService.InitMenu(menuInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建初始化Menu失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建初始化Menu失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("文件变更成功", c)
|
||||
}
|
||||
|
||||
// InitAPI
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/initAPI [post]
|
||||
func (a *AutoCodePluginApi) InitAPI(c *gin.Context) {
|
||||
var apiInfo request.InitApi
|
||||
err := c.ShouldBindJSON(&apiInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodePluginService.InitAPI(apiInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建初始化API失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建初始化API失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("文件变更成功", c)
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeTemplateApi struct{}
|
||||
|
||||
// Preview
|
||||
// @Tags AutoCodeTemplate
|
||||
// @Summary 预览创建后的代码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoCode true "预览创建代码"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "预览创建后的代码"
|
||||
// @Router /autoCode/preview [post]
|
||||
func (a *AutoCodeTemplateApi) Preview(c *gin.Context) {
|
||||
var info request.AutoCode
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(info, utils.AutoCodeVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = info.Pretreatment()
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
info.PackageT = utils.FirstUpper(info.Package)
|
||||
autoCode, err := autoCodeTemplateService.Preview(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error(err.Error(), zap.Error(err))
|
||||
response.FailWithMessage("预览失败:"+err.Error(), c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"autoCode": autoCode}, "预览成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// Create
|
||||
// @Tags AutoCodeTemplate
|
||||
// @Summary 自动代码模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoCode true "创建自动代码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/createTemp [post]
|
||||
func (a *AutoCodeTemplateApi) Create(c *gin.Context) {
|
||||
var info request.AutoCode
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(info, utils.AutoCodeVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = info.Pretreatment()
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodeTemplateService.Create(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// AddFunc
|
||||
// @Tags AddFunc
|
||||
// @Summary 增加方法
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoCode true "增加方法"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/addFunc [post]
|
||||
func (a *AutoCodeTemplateApi) AddFunc(c *gin.Context) {
|
||||
var info request.AutoFunc
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
var tempMap map[string]string
|
||||
if info.IsPreview {
|
||||
info.Router = "填充router"
|
||||
info.FuncName = "填充funcName"
|
||||
info.Method = "填充method"
|
||||
info.Description = "填充description"
|
||||
tempMap, err = autoCodeTemplateService.GetApiAndServer(info)
|
||||
} else {
|
||||
err = autoCodeTemplateService.AddFunc(info)
|
||||
}
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("注入失败!", zap.Error(err))
|
||||
response.FailWithMessage("注入失败", c)
|
||||
} else {
|
||||
if info.IsPreview {
|
||||
response.OkWithDetailed(tempMap, "注入成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("注入成功", c)
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,15 @@ type ApiGroup struct {
|
||||
DictionaryApi
|
||||
AuthorityMenuApi
|
||||
OperationRecordApi
|
||||
AutoCodeHistoryApi
|
||||
DictionaryDetailApi
|
||||
AuthorityBtnApi
|
||||
SysExportTemplateApi
|
||||
AutoCodePluginApi
|
||||
AutoCodePackageApi
|
||||
AutoCodeHistoryApi
|
||||
AutoCodeTemplateApi
|
||||
SysParamsApi
|
||||
SysVersionApi
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -25,12 +32,18 @@ var (
|
||||
userService = service.ServiceGroupApp.SystemServiceGroup.UserService
|
||||
initDBService = service.ServiceGroupApp.SystemServiceGroup.InitDBService
|
||||
casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService
|
||||
autoCodeService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeService
|
||||
baseMenuService = service.ServiceGroupApp.SystemServiceGroup.BaseMenuService
|
||||
authorityService = service.ServiceGroupApp.SystemServiceGroup.AuthorityService
|
||||
dictionaryService = service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
authorityBtnService = service.ServiceGroupApp.SystemServiceGroup.AuthorityBtnService
|
||||
systemConfigService = service.ServiceGroupApp.SystemServiceGroup.SystemConfigService
|
||||
sysParamsService = service.ServiceGroupApp.SystemServiceGroup.SysParamsService
|
||||
operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
|
||||
autoCodeHistoryService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeHistoryService
|
||||
dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
|
||||
autoCodeService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeService
|
||||
autoCodePluginService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePlugin
|
||||
autoCodePackageService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePackage
|
||||
autoCodeHistoryService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeHistory
|
||||
autoCodeTemplateService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate
|
||||
sysVersionService = service.ServiceGroupApp.SystemServiceGroup.SysVersionService
|
||||
)
|
||||
|
||||
+272
-121
@@ -15,158 +15,309 @@ import (
|
||||
|
||||
type SystemApiApi struct{}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 创建基础api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /api/createApi [post]
|
||||
// CreateApi
|
||||
// @Tags SysApi
|
||||
// @Summary 创建基础api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建基础api"
|
||||
// @Router /api/createApi [post]
|
||||
func (s *SystemApiApi) CreateApi(c *gin.Context) {
|
||||
var api system.SysApi
|
||||
_ = c.ShouldBindJSON(&api)
|
||||
if err := utils.Verify(api, utils.ApiVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&api)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := apiService.CreateApi(api); err != nil {
|
||||
err = utils.Verify(api, utils.ApiVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.CreateApi(api)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 删除api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysApi true "ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /api/deleteApi [post]
|
||||
func (s *SystemApiApi) DeleteApi(c *gin.Context) {
|
||||
var api system.SysApi
|
||||
_ = c.ShouldBindJSON(&api)
|
||||
if err := utils.Verify(api.GVA_MODEL, utils.IdVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := apiService.DeleteApi(api); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 分页获取API列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SearchApiParams true "分页获取API列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /api/getApiList [post]
|
||||
func (s *SystemApiApi) GetApiList(c *gin.Context) {
|
||||
var pageInfo systemReq.SearchApiParams
|
||||
_ = c.ShouldBindJSON(&pageInfo)
|
||||
if err := utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
// SyncApi
|
||||
// @Tags SysApi
|
||||
// @Summary 同步API
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "同步API"
|
||||
// @Router /api/syncApi [get]
|
||||
func (s *SystemApiApi) SyncApi(c *gin.Context) {
|
||||
newApis, deleteApis, ignoreApis, err := apiService.SyncApi()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("同步失败!", zap.Error(err))
|
||||
response.FailWithMessage("同步失败", c)
|
||||
return
|
||||
}
|
||||
if err, list, total := apiService.GetAPIInfoList(pageInfo.SysApi, pageInfo.PageInfo, pageInfo.OrderKey, pageInfo.Desc); err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
response.OkWithData(gin.H{
|
||||
"newApis": newApis,
|
||||
"deleteApis": deleteApis,
|
||||
"ignoreApis": ignoreApis,
|
||||
}, c)
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 根据id获取api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "根据id获取api"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /api/getApiById [post]
|
||||
func (s *SystemApiApi) GetApiById(c *gin.Context) {
|
||||
var idInfo request.GetById
|
||||
_ = c.ShouldBindJSON(&idInfo)
|
||||
if err := utils.Verify(idInfo, utils.IdVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err, api := apiService.GetApiById(idInfo.ID)
|
||||
// GetApiGroups
|
||||
// @Tags SysApi
|
||||
// @Summary 获取API分组
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "获取API分组"
|
||||
// @Router /api/getApiGroups [get]
|
||||
func (s *SystemApiApi) GetApiGroups(c *gin.Context) {
|
||||
groups, apiGroupMap, err := apiService.GetApiGroups()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithData(systemRes.SysAPIResponse{Api: api}, c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(gin.H{
|
||||
"groups": groups,
|
||||
"apiGroupMap": apiGroupMap,
|
||||
}, c)
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 创建基础api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
|
||||
// @Router /api/updateApi [post]
|
||||
func (s *SystemApiApi) UpdateApi(c *gin.Context) {
|
||||
var api system.SysApi
|
||||
_ = c.ShouldBindJSON(&api)
|
||||
if err := utils.Verify(api, utils.ApiVerify); err != nil {
|
||||
// IgnoreApi
|
||||
// @Tags IgnoreApi
|
||||
// @Summary 忽略API
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "同步API"
|
||||
// @Router /api/ignoreApi [post]
|
||||
func (s *SystemApiApi) IgnoreApi(c *gin.Context) {
|
||||
var ignoreApi system.SysIgnoreApi
|
||||
err := c.ShouldBindJSON(&ignoreApi)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := apiService.UpdateApi(api); err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage("修改失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("修改成功", c)
|
||||
err = apiService.IgnoreApi(ignoreApi)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("忽略失败!", zap.Error(err))
|
||||
response.FailWithMessage("忽略失败", c)
|
||||
return
|
||||
}
|
||||
response.Ok(c)
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 获取所有的Api 不分页
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /api/getAllApis [post]
|
||||
func (s *SystemApiApi) GetAllApis(c *gin.Context) {
|
||||
if err, apis := apiService.GetAllApis(); err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(systemRes.SysAPIListResponse{Apis: apis}, "获取成功", c)
|
||||
// EnterSyncApi
|
||||
// @Tags SysApi
|
||||
// @Summary 确认同步API
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "确认同步API"
|
||||
// @Router /api/enterSyncApi [post]
|
||||
func (s *SystemApiApi) EnterSyncApi(c *gin.Context) {
|
||||
var syncApi systemRes.SysSyncApis
|
||||
err := c.ShouldBindJSON(&syncApi)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.EnterSyncApi(syncApi)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("忽略失败!", zap.Error(err))
|
||||
response.FailWithMessage("忽略失败", c)
|
||||
return
|
||||
}
|
||||
response.Ok(c)
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 删除选中Api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /api/deleteApisByIds [delete]
|
||||
func (s *SystemApiApi) DeleteApisByIds(c *gin.Context) {
|
||||
var ids request.IdsReq
|
||||
_ = c.ShouldBindJSON(&ids)
|
||||
if err := apiService.DeleteApisByIds(ids); err != nil {
|
||||
// DeleteApi
|
||||
// @Tags SysApi
|
||||
// @Summary 删除api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysApi true "ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除api"
|
||||
// @Router /api/deleteApi [post]
|
||||
func (s *SystemApiApi) DeleteApi(c *gin.Context) {
|
||||
var api system.SysApi
|
||||
err := c.ShouldBindJSON(&api)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(api.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.DeleteApi(api)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// GetApiList
|
||||
// @Tags SysApi
|
||||
// @Summary 分页获取API列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SearchApiParams true "分页获取API列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取API列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /api/getApiList [post]
|
||||
func (s *SystemApiApi) GetApiList(c *gin.Context) {
|
||||
var pageInfo systemReq.SearchApiParams
|
||||
err := c.ShouldBindJSON(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := apiService.GetAPIInfoList(pageInfo.SysApi, pageInfo.PageInfo, pageInfo.OrderKey, pageInfo.Desc)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetApiById
|
||||
// @Tags SysApi
|
||||
// @Summary 根据id获取api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "根据id获取api"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysAPIResponse} "根据id获取api,返回包括api详情"
|
||||
// @Router /api/getApiById [post]
|
||||
func (s *SystemApiApi) GetApiById(c *gin.Context) {
|
||||
var idInfo request.GetById
|
||||
err := c.ShouldBindJSON(&idInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(idInfo, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
api, err := apiService.GetApiById(idInfo.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysAPIResponse{Api: api}, "获取成功", c)
|
||||
}
|
||||
|
||||
// UpdateApi
|
||||
// @Tags SysApi
|
||||
// @Summary 修改基础api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法"
|
||||
// @Success 200 {object} response.Response{msg=string} "修改基础api"
|
||||
// @Router /api/updateApi [post]
|
||||
func (s *SystemApiApi) UpdateApi(c *gin.Context) {
|
||||
var api system.SysApi
|
||||
err := c.ShouldBindJSON(&api)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(api, utils.ApiVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.UpdateApi(api)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage("修改失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// GetAllApis
|
||||
// @Tags SysApi
|
||||
// @Summary 获取所有的Api 不分页
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysAPIListResponse,msg=string} "获取所有的Api 不分页,返回包括api列表"
|
||||
// @Router /api/getAllApis [post]
|
||||
func (s *SystemApiApi) GetAllApis(c *gin.Context) {
|
||||
authorityID := utils.GetUserAuthorityId(c)
|
||||
apis, err := apiService.GetAllApis(authorityID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysAPIListResponse{Apis: apis}, "获取成功", c)
|
||||
}
|
||||
|
||||
// DeleteApisByIds
|
||||
// @Tags SysApi
|
||||
// @Summary 删除选中Api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除选中Api"
|
||||
// @Router /api/deleteApisByIds [delete]
|
||||
func (s *SystemApiApi) DeleteApisByIds(c *gin.Context) {
|
||||
var ids request.IdsReq
|
||||
err := c.ShouldBindJSON(&ids)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.DeleteApisByIds(ids)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// FreshCasbin
|
||||
// @Tags SysApi
|
||||
// @Summary 刷新casbin缓存
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "刷新成功"
|
||||
// @Router /api/freshCasbin [get]
|
||||
func (s *SystemApiApi) FreshCasbin(c *gin.Context) {
|
||||
err := casbinService.FreshCasbin()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("刷新失败!", zap.Error(err))
|
||||
response.FailWithMessage("刷新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("刷新成功", c)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,8 @@ package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
|
||||
@@ -15,151 +13,190 @@ import (
|
||||
|
||||
type AuthorityApi struct{}
|
||||
|
||||
// @Tags Authority
|
||||
// @Summary 创建角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /authority/createAuthority [post]
|
||||
// CreateAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 创建角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "创建角色,返回包括系统角色详情"
|
||||
// @Router /authority/createAuthority [post]
|
||||
func (a *AuthorityApi) CreateAuthority(c *gin.Context) {
|
||||
var authority system.SysAuthority
|
||||
_ = c.ShouldBindJSON(&authority)
|
||||
if err := utils.Verify(authority, utils.AuthorityVerify); err != nil {
|
||||
var authority, authBack system.SysAuthority
|
||||
var err error
|
||||
|
||||
if err = c.ShouldBindJSON(&authority); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, authBack := authorityService.CreateAuthority(authority); err != nil {
|
||||
|
||||
if err = utils.Verify(authority, utils.AuthorityVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if *authority.ParentId == 0 && global.GVA_CONFIG.System.UseStrictAuth {
|
||||
authority.ParentId = utils.Pointer(utils.GetUserAuthorityId(c))
|
||||
}
|
||||
|
||||
if authBack, err = authorityService.CreateAuthority(authority); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败"+err.Error(), c)
|
||||
} else {
|
||||
_ = menuService.AddMenuAuthority(systemReq.DefaultMenu(), authority.AuthorityId)
|
||||
_ = casbinService.UpdateCasbin(authority.AuthorityId, systemReq.DefaultCasbin())
|
||||
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "创建成功", c)
|
||||
return
|
||||
}
|
||||
err = casbinService.FreshCasbin()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建成功,权限刷新失败。", zap.Error(err))
|
||||
response.FailWithMessage("创建成功,权限刷新失败。"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "创建成功", c)
|
||||
}
|
||||
|
||||
// @Tags Authority
|
||||
// @Summary 拷贝角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body response.SysAuthorityCopyResponse true "旧角色id, 新权限id, 新权限名, 新父角色id"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"拷贝成功"}"
|
||||
// @Router /authority/copyAuthority [post]
|
||||
// CopyAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 拷贝角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body response.SysAuthorityCopyResponse true "旧角色id, 新权限id, 新权限名, 新父角色id"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "拷贝角色,返回包括系统角色详情"
|
||||
// @Router /authority/copyAuthority [post]
|
||||
func (a *AuthorityApi) CopyAuthority(c *gin.Context) {
|
||||
var copyInfo systemRes.SysAuthorityCopyResponse
|
||||
_ = c.ShouldBindJSON(©Info)
|
||||
if err := utils.Verify(copyInfo, utils.OldAuthorityVerify); err != nil {
|
||||
err := c.ShouldBindJSON(©Info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := utils.Verify(copyInfo.Authority, utils.AuthorityVerify); err != nil {
|
||||
err = utils.Verify(copyInfo, utils.OldAuthorityVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, authBack := authorityService.CopyAuthority(copyInfo); err != nil {
|
||||
err = utils.Verify(copyInfo.Authority, utils.AuthorityVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
adminAuthorityID := utils.GetUserAuthorityId(c)
|
||||
authBack, err := authorityService.CopyAuthority(adminAuthorityID, copyInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("拷贝失败!", zap.Error(err))
|
||||
response.FailWithMessage("拷贝失败"+err.Error(), c)
|
||||
} else {
|
||||
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "拷贝成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "拷贝成功", c)
|
||||
}
|
||||
|
||||
// @Tags Authority
|
||||
// @Summary 删除角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "删除角色"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /authority/deleteAuthority [post]
|
||||
// DeleteAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 删除角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "删除角色"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除角色"
|
||||
// @Router /authority/deleteAuthority [post]
|
||||
func (a *AuthorityApi) DeleteAuthority(c *gin.Context) {
|
||||
var authority system.SysAuthority
|
||||
_ = c.ShouldBindJSON(&authority)
|
||||
if err := utils.Verify(authority, utils.AuthorityIdVerify); err != nil {
|
||||
var err error
|
||||
if err = c.ShouldBindJSON(&authority); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := authorityService.DeleteAuthority(&authority); err != nil { // 删除角色之前需要判断是否有用户正在使用此角色
|
||||
if err = utils.Verify(authority, utils.AuthorityIdVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
// 删除角色之前需要判断是否有用户正在使用此角色
|
||||
if err = authorityService.DeleteAuthority(&authority); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败"+err.Error(), c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
return
|
||||
}
|
||||
_ = casbinService.FreshCasbin()
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// @Tags Authority
|
||||
// @Summary 更新角色信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /authority/updateAuthority [post]
|
||||
// UpdateAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 更新角色信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "更新角色信息,返回包括系统角色详情"
|
||||
// @Router /authority/updateAuthority [put]
|
||||
func (a *AuthorityApi) UpdateAuthority(c *gin.Context) {
|
||||
var auth system.SysAuthority
|
||||
_ = c.ShouldBindJSON(&auth)
|
||||
if err := utils.Verify(auth, utils.AuthorityVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&auth)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, authority := authorityService.UpdateAuthority(auth); err != nil {
|
||||
err = utils.Verify(auth, utils.AuthorityVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
authority, err := authorityService.UpdateAuthority(auth)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败"+err.Error(), c)
|
||||
} else {
|
||||
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authority}, "更新成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags Authority
|
||||
// @Summary 分页获取角色列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /authority/getAuthorityList [post]
|
||||
func (a *AuthorityApi) GetAuthorityList(c *gin.Context) {
|
||||
var pageInfo request.PageInfo
|
||||
_ = c.ShouldBindJSON(&pageInfo)
|
||||
if err := utils.Verify(pageInfo, utils.PageInfoVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, list, total := authorityService.GetAuthorityInfoList(pageInfo); err != nil {
|
||||
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authority}, "更新成功", c)
|
||||
}
|
||||
|
||||
// GetAuthorityList
|
||||
// @Tags Authority
|
||||
// @Summary 分页获取角色列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取角色列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /authority/getAuthorityList [post]
|
||||
func (a *AuthorityApi) GetAuthorityList(c *gin.Context) {
|
||||
authorityID := utils.GetUserAuthorityId(c)
|
||||
list, err := authorityService.GetAuthorityInfoList(authorityID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败"+err.Error(), c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(list, "获取成功", c)
|
||||
}
|
||||
|
||||
// @Tags Authority
|
||||
// @Summary 设置角色资源权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "设置角色资源权限"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
|
||||
// @Router /authority/setDataAuthority [post]
|
||||
// SetDataAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 设置角色资源权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "设置角色资源权限"
|
||||
// @Success 200 {object} response.Response{msg=string} "设置角色资源权限"
|
||||
// @Router /authority/setDataAuthority [post]
|
||||
func (a *AuthorityApi) SetDataAuthority(c *gin.Context) {
|
||||
var auth system.SysAuthority
|
||||
_ = c.ShouldBindJSON(&auth)
|
||||
if err := utils.Verify(auth, utils.AuthorityIdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&auth)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := authorityService.SetDataAuthority(auth); err != nil {
|
||||
err = utils.Verify(auth, utils.AuthorityIdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
adminAuthorityID := utils.GetUserAuthorityId(c)
|
||||
err = authorityService.SetDataAuthority(adminAuthorityID, auth)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败"+err.Error(), c)
|
||||
} else {
|
||||
response.OkWithMessage("设置成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AuthorityBtnApi struct{}
|
||||
|
||||
// GetAuthorityBtn
|
||||
// @Tags AuthorityBtn
|
||||
// @Summary 获取权限按钮
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.SysAuthorityBtnReq true "菜单id, 角色id, 选中的按钮id"
|
||||
// @Success 200 {object} response.Response{data=response.SysAuthorityBtnRes,msg=string} "返回列表成功"
|
||||
// @Router /authorityBtn/getAuthorityBtn [post]
|
||||
func (a *AuthorityBtnApi) GetAuthorityBtn(c *gin.Context) {
|
||||
var req request.SysAuthorityBtnReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
res, err := authorityBtnService.GetAuthorityBtn(req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(res, "查询成功", c)
|
||||
}
|
||||
|
||||
// SetAuthorityBtn
|
||||
// @Tags AuthorityBtn
|
||||
// @Summary 设置权限按钮
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.SysAuthorityBtnReq true "菜单id, 角色id, 选中的按钮id"
|
||||
// @Success 200 {object} response.Response{msg=string} "返回列表成功"
|
||||
// @Router /authorityBtn/setAuthorityBtn [post]
|
||||
func (a *AuthorityBtnApi) SetAuthorityBtn(c *gin.Context) {
|
||||
var req request.SysAuthorityBtnReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = authorityBtnService.SetAuthorityBtn(req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("分配失败!", zap.Error(err))
|
||||
response.FailWithMessage("分配失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("分配成功", c)
|
||||
}
|
||||
|
||||
// CanRemoveAuthorityBtn
|
||||
// @Tags AuthorityBtn
|
||||
// @Summary 设置权限按钮
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /authorityBtn/canRemoveAuthorityBtn [post]
|
||||
func (a *AuthorityBtnApi) CanRemoveAuthorityBtn(c *gin.Context) {
|
||||
id := c.Query("id")
|
||||
err := authorityBtnService.CanRemoveAuthorityBtn(id)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
@@ -1,121 +1,72 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
"github.com/goccy/go-json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeApi struct{}
|
||||
|
||||
// PreviewTemp
|
||||
// @Tags AutoCode
|
||||
// @Summary 预览创建后的代码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.AutoCodeStruct true "预览创建代码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/preview [post]
|
||||
func (autoApi *AutoCodeApi) PreviewTemp(c *gin.Context) {
|
||||
var a system.AutoCodeStruct
|
||||
_ = c.ShouldBindJSON(&a)
|
||||
if err := utils.Verify(a, utils.AutoCodeVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
autoCode, err := autoCodeService.PreviewTemp(a)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("预览失败!", zap.Error(err))
|
||||
response.FailWithMessage("预览失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"autoCode": autoCode}, "预览成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTemp
|
||||
// @Tags AutoCode
|
||||
// @Summary 自动代码模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.AutoCodeStruct true "创建自动代码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/createTemp [post]
|
||||
func (autoApi *AutoCodeApi) CreateTemp(c *gin.Context) {
|
||||
var a system.AutoCodeStruct
|
||||
_ = c.ShouldBindJSON(&a)
|
||||
if err := utils.Verify(a, utils.AutoCodeVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
var apiIds []uint
|
||||
if a.AutoCreateApiToSql {
|
||||
if ids, err := autoCodeService.AutoCreateApi(&a); err != nil {
|
||||
global.GVA_LOG.Error("自动化创建失败!请自行清空垃圾数据!", zap.Error(err))
|
||||
c.Writer.Header().Add("success", "false")
|
||||
c.Writer.Header().Add("msg", url.QueryEscape("自动化创建失败!请自行清空垃圾数据!"))
|
||||
return
|
||||
} else {
|
||||
apiIds = ids
|
||||
}
|
||||
}
|
||||
err := autoCodeService.CreateTemp(a, apiIds...)
|
||||
if err != nil {
|
||||
if errors.Is(err, system.AutoMoveErr) {
|
||||
c.Writer.Header().Add("success", "true")
|
||||
c.Writer.Header().Add("msg", url.QueryEscape(err.Error()))
|
||||
} else {
|
||||
c.Writer.Header().Add("success", "false")
|
||||
c.Writer.Header().Add("msg", url.QueryEscape(err.Error()))
|
||||
_ = os.Remove("./ginvueadmin.zip")
|
||||
}
|
||||
} else {
|
||||
c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", "ginvueadmin.zip")) // fmt.Sprintf("attachment; filename=%s", filename)对下载的文件重命名
|
||||
c.Writer.Header().Add("Content-Type", "application/json")
|
||||
c.Writer.Header().Add("success", "true")
|
||||
c.File("./ginvueadmin.zip")
|
||||
_ = os.Remove("./ginvueadmin.zip")
|
||||
}
|
||||
}
|
||||
|
||||
// GetDB
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取当前所有数据库
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /autoCode/getDatabase [get]
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取当前所有数据库
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前所有数据库"
|
||||
// @Router /autoCode/getDB [get]
|
||||
func (autoApi *AutoCodeApi) GetDB(c *gin.Context) {
|
||||
dbs, err := autoCodeService.Database().GetDB()
|
||||
businessDB := c.Query("businessDB")
|
||||
dbs, err := autoCodeService.Database(businessDB).GetDB(businessDB)
|
||||
var dbList []map[string]interface{}
|
||||
for _, db := range global.GVA_CONFIG.DBList {
|
||||
var item = make(map[string]interface{})
|
||||
item["aliasName"] = db.AliasName
|
||||
item["dbName"] = db.Dbname
|
||||
item["disable"] = db.Disable
|
||||
item["dbtype"] = db.Type
|
||||
dbList = append(dbList, item)
|
||||
}
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"dbs": dbs, "dbList": dbList}, "获取成功", c)
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"dbs": dbs}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetTables
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取当前数据库所有表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /autoCode/getTables [get]
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取当前数据库所有表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前数据库所有表"
|
||||
// @Router /autoCode/getTables [get]
|
||||
func (autoApi *AutoCodeApi) GetTables(c *gin.Context) {
|
||||
dbName := c.DefaultQuery("dbName", global.GVA_CONFIG.Mysql.Dbname)
|
||||
tables, err := autoCodeService.Database().GetTables(dbName)
|
||||
dbName := c.Query("dbName")
|
||||
businessDB := c.Query("businessDB")
|
||||
if dbName == "" {
|
||||
dbName = *global.GVA_ACTIVE_DBNAME
|
||||
if businessDB != "" {
|
||||
for _, db := range global.GVA_CONFIG.DBList {
|
||||
if db.AliasName == businessDB {
|
||||
dbName = db.Dbname
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tables, err := autoCodeService.Database(businessDB).GetTables(businessDB, dbName)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询table失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询table失败", c)
|
||||
@@ -125,20 +76,80 @@ func (autoApi *AutoCodeApi) GetTables(c *gin.Context) {
|
||||
}
|
||||
|
||||
// GetColumn
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取当前表所有字段
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /autoCode/getColumn [get]
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取当前表所有字段
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前表所有字段"
|
||||
// @Router /autoCode/getColumn [get]
|
||||
func (autoApi *AutoCodeApi) GetColumn(c *gin.Context) {
|
||||
dbName := c.DefaultQuery("dbName", global.GVA_CONFIG.Mysql.Dbname)
|
||||
businessDB := c.Query("businessDB")
|
||||
dbName := c.Query("dbName")
|
||||
if dbName == "" {
|
||||
dbName = *global.GVA_ACTIVE_DBNAME
|
||||
if businessDB != "" {
|
||||
for _, db := range global.GVA_CONFIG.DBList {
|
||||
if db.AliasName == businessDB {
|
||||
dbName = db.Dbname
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tableName := c.Query("tableName")
|
||||
columns, err := autoCodeService.Database().GetColumn(tableName, dbName)
|
||||
columns, err := autoCodeService.Database(businessDB).GetColumn(businessDB, tableName, dbName)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"columns": columns}, "获取成功", c)
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"columns": columns}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (autoApi *AutoCodeApi) LLMAuto(c *gin.Context) {
|
||||
var llm common.JSONMap
|
||||
err := c.ShouldBindJSON(&llm)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if global.GVA_CONFIG.AutoCode.AiPath == "" {
|
||||
response.FailWithMessage("请先前往插件市场个人中心获取AiPath并填入config.yaml中", c)
|
||||
return
|
||||
}
|
||||
|
||||
path := strings.ReplaceAll(global.GVA_CONFIG.AutoCode.AiPath, "{FUNC}", fmt.Sprintf("api/chat/%s", llm["mode"]))
|
||||
res, err := request.HttpRequest(
|
||||
path,
|
||||
"POST",
|
||||
nil,
|
||||
nil,
|
||||
llm,
|
||||
)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("大模型生成失败!", zap.Error(err))
|
||||
response.FailWithMessage("大模型生成失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
var resStruct response.Response
|
||||
b, err := io.ReadAll(res.Body)
|
||||
defer res.Body.Close()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("大模型生成失败!", zap.Error(err))
|
||||
response.FailWithMessage("大模型生成失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(b, &resStruct)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("大模型生成失败!", zap.Error(err))
|
||||
response.FailWithMessage("大模型生成失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if resStruct.Code == 7 {
|
||||
global.GVA_LOG.Error("大模型生成失败!"+resStruct.Msg, zap.Error(err))
|
||||
response.FailWithMessage("大模型生成失败"+resStruct.Msg, c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(resStruct.Data, c)
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeHistoryApi struct{}
|
||||
|
||||
// First
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取meta信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "请求参数"
|
||||
// @Success 200 {object} response.Response{} "获取成功!"
|
||||
// @Router /autoCode/getMeta [post]
|
||||
func (a *AutoCodeHistoryApi) First(c *gin.Context) {
|
||||
var info request.GetById
|
||||
_ = c.ShouldBindJSON(&info)
|
||||
data, err := autoCodeHistoryService.First(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"meta": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
// Delete
|
||||
// @Tags AutoCode
|
||||
// @Summary 删除回滚记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "请求参数"
|
||||
// @Success 200 {object} response.Response{} "删除成功!"
|
||||
// @Router /autoCode/delSysHistory [post]
|
||||
func (a *AutoCodeHistoryApi) Delete(c *gin.Context) {
|
||||
var info request.GetById
|
||||
_ = c.ShouldBindJSON(&info)
|
||||
err := autoCodeHistoryService.Delete(&info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// RollBack
|
||||
// @Tags AutoCode
|
||||
// @Summary 回滚自动生成代码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "请求参数"
|
||||
// @Success 200 {object} response.Response{} "回滚成功!"
|
||||
// @Router /autoCode/rollback [post]
|
||||
func (a *AutoCodeHistoryApi) RollBack(c *gin.Context) {
|
||||
var info request.GetById
|
||||
_ = c.ShouldBindJSON(&info)
|
||||
if err := autoCodeHistoryService.RollBack(&info); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("回滚成功", c)
|
||||
}
|
||||
|
||||
// GetList
|
||||
// @Tags AutoCode
|
||||
// @Summary 查询回滚记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SysAutoHistory true "请求参数"
|
||||
// @Success 200 {object} response.Response{} "获取成功!"
|
||||
// @Router /autoCode/getSysHistory [post]
|
||||
func (a *AutoCodeHistoryApi) GetList(c *gin.Context) {
|
||||
var search systemReq.SysAutoHistory
|
||||
_ = c.ShouldBindJSON(&search)
|
||||
list, total, err := autoCodeHistoryService.GetList(search.PageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: search.Page,
|
||||
PageSize: search.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
|
||||
@@ -15,27 +17,54 @@ var store = base64Captcha.DefaultMemStore
|
||||
|
||||
type BaseApi struct{}
|
||||
|
||||
// @Tags Base
|
||||
// @Summary 生成验证码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"验证码获取成功"}"
|
||||
// @Router /base/captcha [post]
|
||||
// Captcha
|
||||
// @Tags Base
|
||||
// @Summary 生成验证码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysCaptchaResponse,msg=string} "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码"
|
||||
// @Router /base/captcha [post]
|
||||
func (b *BaseApi) Captcha(c *gin.Context) {
|
||||
// 判断验证码是否开启
|
||||
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
|
||||
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
|
||||
key := c.ClientIP()
|
||||
v, ok := global.BlackCache.Get(key)
|
||||
if !ok {
|
||||
global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
|
||||
}
|
||||
|
||||
var oc bool
|
||||
if openCaptcha == 0 || openCaptcha < interfaceToInt(v) {
|
||||
oc = true
|
||||
}
|
||||
// 字符,公式,验证码配置
|
||||
// 生成默认数字的driver
|
||||
driver := base64Captcha.NewDriverDigit(global.GVA_CONFIG.Captcha.ImgHeight, global.GVA_CONFIG.Captcha.ImgWidth, global.GVA_CONFIG.Captcha.KeyLong, 0.7, 80)
|
||||
// cp := base64Captcha.NewCaptcha(driver, store.UseWithCtx(c)) // v8下使用redis
|
||||
cp := base64Captcha.NewCaptcha(driver, store)
|
||||
if id, b64s, err := cp.Generate(); err != nil {
|
||||
id, b64s, _, err := cp.Generate()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("验证码获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("验证码获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(systemRes.SysCaptchaResponse{
|
||||
CaptchaId: id,
|
||||
PicPath: b64s,
|
||||
CaptchaLength: global.GVA_CONFIG.Captcha.KeyLong,
|
||||
}, "验证码获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysCaptchaResponse{
|
||||
CaptchaId: id,
|
||||
PicPath: b64s,
|
||||
CaptchaLength: global.GVA_CONFIG.Captcha.KeyLong,
|
||||
OpenCaptcha: oc,
|
||||
}, "验证码获取成功", c)
|
||||
}
|
||||
|
||||
// 类型转换
|
||||
func interfaceToInt(v interface{}) (i int) {
|
||||
switch v := v.(type) {
|
||||
case int:
|
||||
i = v
|
||||
default:
|
||||
i = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -12,41 +12,55 @@ import (
|
||||
|
||||
type CasbinApi struct{}
|
||||
|
||||
// @Tags Casbin
|
||||
// @Summary 更新角色api权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CasbinInReceive true "权限id, 权限模型列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /casbin/UpdateCasbin [post]
|
||||
// UpdateCasbin
|
||||
// @Tags Casbin
|
||||
// @Summary 更新角色api权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CasbinInReceive true "权限id, 权限模型列表"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新角色api权限"
|
||||
// @Router /casbin/UpdateCasbin [post]
|
||||
func (cas *CasbinApi) UpdateCasbin(c *gin.Context) {
|
||||
var cmr request.CasbinInReceive
|
||||
_ = c.ShouldBindJSON(&cmr)
|
||||
if err := utils.Verify(cmr, utils.AuthorityIdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&cmr)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := casbinService.UpdateCasbin(cmr.AuthorityId, cmr.CasbinInfos); err != nil {
|
||||
err = utils.Verify(cmr, utils.AuthorityIdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
adminAuthorityID := utils.GetUserAuthorityId(c)
|
||||
err = casbinService.UpdateCasbin(adminAuthorityID, cmr.AuthorityId, cmr.CasbinInfos)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// @Tags Casbin
|
||||
// @Summary 获取权限列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CasbinInReceive true "权限id, 权限模型列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /casbin/getPolicyPathByAuthorityId [post]
|
||||
// GetPolicyPathByAuthorityId
|
||||
// @Tags Casbin
|
||||
// @Summary 获取权限列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CasbinInReceive true "权限id, 权限模型列表"
|
||||
// @Success 200 {object} response.Response{data=systemRes.PolicyPathResponse,msg=string} "获取权限列表,返回包括casbin详情列表"
|
||||
// @Router /casbin/getPolicyPathByAuthorityId [post]
|
||||
func (cas *CasbinApi) GetPolicyPathByAuthorityId(c *gin.Context) {
|
||||
var casbin request.CasbinInReceive
|
||||
_ = c.ShouldBindJSON(&casbin)
|
||||
if err := utils.Verify(casbin, utils.AuthorityIdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&casbin)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(casbin, utils.AuthorityIdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,113 +5,133 @@ import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type DictionaryApi struct{}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 创建SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionary true "SysDictionary模型"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /sysDictionary/createSysDictionary [post]
|
||||
// CreateSysDictionary
|
||||
// @Tags SysDictionary
|
||||
// @Summary 创建SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionary true "SysDictionary模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建SysDictionary"
|
||||
// @Router /sysDictionary/createSysDictionary [post]
|
||||
func (s *DictionaryApi) CreateSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
_ = c.ShouldBindJSON(&dictionary)
|
||||
if err := dictionaryService.CreateSysDictionary(dictionary); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 删除SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionary true "SysDictionary模型"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysDictionary/deleteSysDictionary [delete]
|
||||
func (s *DictionaryApi) DeleteSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
_ = c.ShouldBindJSON(&dictionary)
|
||||
if err := dictionaryService.DeleteSysDictionary(dictionary); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 更新SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionary true "SysDictionary模型"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /sysDictionary/updateSysDictionary [put]
|
||||
func (s *DictionaryApi) UpdateSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
_ = c.ShouldBindJSON(&dictionary)
|
||||
if err := dictionaryService.UpdateSysDictionary(&dictionary); err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 用id查询SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysDictionary true "ID或字典英名"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /sysDictionary/findSysDictionary [get]
|
||||
func (s *DictionaryApi) FindSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
_ = c.ShouldBindQuery(&dictionary)
|
||||
if err, sysDictionary := dictionaryService.GetSysDictionary(dictionary.Type, dictionary.ID); err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"resysDictionary": sysDictionary}, "查询成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 分页获取SysDictionary列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.SysDictionarySearch true "页码, 每页大小, 搜索条件"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysDictionary/getSysDictionaryList [get]
|
||||
func (s *DictionaryApi) GetSysDictionaryList(c *gin.Context) {
|
||||
var pageInfo request.SysDictionarySearch
|
||||
_ = c.ShouldBindQuery(&pageInfo)
|
||||
if err := utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, list, total := dictionaryService.GetSysDictionaryInfoList(pageInfo); err != nil {
|
||||
err = dictionaryService.CreateSysDictionary(dictionary)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysDictionary
|
||||
// @Tags SysDictionary
|
||||
// @Summary 删除SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionary true "SysDictionary模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除SysDictionary"
|
||||
// @Router /sysDictionary/deleteSysDictionary [delete]
|
||||
func (s *DictionaryApi) DeleteSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
err := c.ShouldBindJSON(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryService.DeleteSysDictionary(dictionary)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateSysDictionary
|
||||
// @Tags SysDictionary
|
||||
// @Summary 更新SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionary true "SysDictionary模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新SysDictionary"
|
||||
// @Router /sysDictionary/updateSysDictionary [put]
|
||||
func (s *DictionaryApi) UpdateSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
err := c.ShouldBindJSON(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryService.UpdateSysDictionary(&dictionary)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindSysDictionary
|
||||
// @Tags SysDictionary
|
||||
// @Summary 用id查询SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysDictionary true "ID或字典英名"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysDictionary"
|
||||
// @Router /sysDictionary/findSysDictionary [get]
|
||||
func (s *DictionaryApi) FindSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
err := c.ShouldBindQuery(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
sysDictionary, err := dictionaryService.GetSysDictionary(dictionary.Type, dictionary.ID, dictionary.Status)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("字典未创建或未开启!", zap.Error(err))
|
||||
response.FailWithMessage("字典未创建或未开启", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"resysDictionary": sysDictionary}, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetSysDictionaryList
|
||||
// @Tags SysDictionary
|
||||
// @Summary 分页获取SysDictionary列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.SysDictionarySearch true "字典 name 或者 type"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /sysDictionary/getSysDictionaryList [get]
|
||||
func (s *DictionaryApi) GetSysDictionaryList(c *gin.Context) {
|
||||
var dictionary request.SysDictionarySearch
|
||||
err := c.ShouldBindQuery(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, err := dictionaryService.GetSysDictionaryInfoList(c, dictionary)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(list, "获取成功", c)
|
||||
}
|
||||
|
||||
@@ -12,106 +12,137 @@ import (
|
||||
|
||||
type DictionaryDetailApi struct{}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 创建SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /sysDictionaryDetail/createSysDictionaryDetail [post]
|
||||
// CreateSysDictionaryDetail
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 创建SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建SysDictionaryDetail"
|
||||
// @Router /sysDictionaryDetail/createSysDictionaryDetail [post]
|
||||
func (s *DictionaryDetailApi) CreateSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
_ = c.ShouldBindJSON(&detail)
|
||||
if err := dictionaryDetailService.CreateSysDictionaryDetail(detail); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 删除SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysDictionaryDetail/deleteSysDictionaryDetail [delete]
|
||||
func (s *DictionaryDetailApi) DeleteSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
_ = c.ShouldBindJSON(&detail)
|
||||
if err := dictionaryDetailService.DeleteSysDictionaryDetail(detail); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 更新SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionaryDetail true "更新SysDictionaryDetail"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /sysDictionaryDetail/updateSysDictionaryDetail [put]
|
||||
func (s *DictionaryDetailApi) UpdateSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
_ = c.ShouldBindJSON(&detail)
|
||||
if err := dictionaryDetailService.UpdateSysDictionaryDetail(&detail); err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 用id查询SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysDictionaryDetail true "用id查询SysDictionaryDetail"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /sysDictionaryDetail/findSysDictionaryDetail [get]
|
||||
func (s *DictionaryDetailApi) FindSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
_ = c.ShouldBindQuery(&detail)
|
||||
if err := utils.Verify(detail, utils.IdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&detail)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, resysDictionaryDetail := dictionaryDetailService.GetSysDictionaryDetail(detail.ID); err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"resysDictionaryDetail": resysDictionaryDetail}, "查询成功", c)
|
||||
err = dictionaryDetailService.CreateSysDictionaryDetail(detail)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 分页获取SysDictionaryDetail列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.SysDictionaryDetailSearch true "页码, 每页大小, 搜索条件"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysDictionaryDetail/getSysDictionaryDetailList [get]
|
||||
// DeleteSysDictionaryDetail
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 删除SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除SysDictionaryDetail"
|
||||
// @Router /sysDictionaryDetail/deleteSysDictionaryDetail [delete]
|
||||
func (s *DictionaryDetailApi) DeleteSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
err := c.ShouldBindJSON(&detail)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryDetailService.DeleteSysDictionaryDetail(detail)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateSysDictionaryDetail
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 更新SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionaryDetail true "更新SysDictionaryDetail"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新SysDictionaryDetail"
|
||||
// @Router /sysDictionaryDetail/updateSysDictionaryDetail [put]
|
||||
func (s *DictionaryDetailApi) UpdateSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
err := c.ShouldBindJSON(&detail)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryDetailService.UpdateSysDictionaryDetail(&detail)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindSysDictionaryDetail
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 用id查询SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysDictionaryDetail true "用id查询SysDictionaryDetail"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysDictionaryDetail"
|
||||
// @Router /sysDictionaryDetail/findSysDictionaryDetail [get]
|
||||
func (s *DictionaryDetailApi) FindSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
err := c.ShouldBindQuery(&detail)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(detail, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
reSysDictionaryDetail, err := dictionaryDetailService.GetSysDictionaryDetail(detail.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"reSysDictionaryDetail": reSysDictionaryDetail}, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetSysDictionaryDetailList
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 分页获取SysDictionaryDetail列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.SysDictionaryDetailSearch true "页码, 每页大小, 搜索条件"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionaryDetail列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /sysDictionaryDetail/getSysDictionaryDetailList [get]
|
||||
func (s *DictionaryDetailApi) GetSysDictionaryDetailList(c *gin.Context) {
|
||||
var pageInfo request.SysDictionaryDetailSearch
|
||||
_ = c.ShouldBindQuery(&pageInfo)
|
||||
if err, list, total := dictionaryDetailService.GetSysDictionaryDetailInfoList(pageInfo); err != nil {
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := dictionaryDetailService.GetSysDictionaryDetailInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 用于token一次性存储
|
||||
var (
|
||||
exportTokenCache = make(map[string]interface{})
|
||||
exportTokenExpiration = make(map[string]time.Time)
|
||||
tokenMutex sync.RWMutex
|
||||
)
|
||||
|
||||
// 五分钟检测窗口过期
|
||||
func cleanupExpiredTokens() {
|
||||
for {
|
||||
time.Sleep(5 * time.Minute)
|
||||
tokenMutex.Lock()
|
||||
now := time.Now()
|
||||
for token, expiry := range exportTokenExpiration {
|
||||
if now.After(expiry) {
|
||||
delete(exportTokenCache, token)
|
||||
delete(exportTokenExpiration, token)
|
||||
}
|
||||
}
|
||||
tokenMutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
go cleanupExpiredTokens()
|
||||
}
|
||||
|
||||
type SysExportTemplateApi struct {
|
||||
}
|
||||
|
||||
var sysExportTemplateService = service.ServiceGroupApp.SystemServiceGroup.SysExportTemplateService
|
||||
|
||||
// CreateSysExportTemplate 创建导出模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 创建导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysExportTemplate true "创建导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /sysExportTemplate/createSysExportTemplate [post]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) CreateSysExportTemplate(c *gin.Context) {
|
||||
var sysExportTemplate system.SysExportTemplate
|
||||
err := c.ShouldBindJSON(&sysExportTemplate)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
verify := utils.Rules{
|
||||
"Name": {utils.NotEmpty()},
|
||||
}
|
||||
if err := utils.Verify(sysExportTemplate, verify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := sysExportTemplateService.CreateSysExportTemplate(&sysExportTemplate); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteSysExportTemplate 删除导出模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 删除导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysExportTemplate true "删除导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysExportTemplate/deleteSysExportTemplate [delete]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplate(c *gin.Context) {
|
||||
var sysExportTemplate system.SysExportTemplate
|
||||
err := c.ShouldBindJSON(&sysExportTemplate)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := sysExportTemplateService.DeleteSysExportTemplate(sysExportTemplate); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteSysExportTemplateByIds 批量删除导出模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 批量删除导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}"
|
||||
// @Router /sysExportTemplate/deleteSysExportTemplateByIds [delete]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplateByIds(c *gin.Context) {
|
||||
var IDS request.IdsReq
|
||||
err := c.ShouldBindJSON(&IDS)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := sysExportTemplateService.DeleteSysExportTemplateByIds(IDS); err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateSysExportTemplate 更新导出模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 更新导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysExportTemplate true "更新导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /sysExportTemplate/updateSysExportTemplate [put]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) UpdateSysExportTemplate(c *gin.Context) {
|
||||
var sysExportTemplate system.SysExportTemplate
|
||||
err := c.ShouldBindJSON(&sysExportTemplate)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
verify := utils.Rules{
|
||||
"Name": {utils.NotEmpty()},
|
||||
}
|
||||
if err := utils.Verify(sysExportTemplate, verify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := sysExportTemplateService.UpdateSysExportTemplate(sysExportTemplate); err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// FindSysExportTemplate 用id查询导出模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 用id查询导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysExportTemplate true "用id查询导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /sysExportTemplate/findSysExportTemplate [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) FindSysExportTemplate(c *gin.Context) {
|
||||
var sysExportTemplate system.SysExportTemplate
|
||||
err := c.ShouldBindQuery(&sysExportTemplate)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if resysExportTemplate, err := sysExportTemplateService.GetSysExportTemplate(sysExportTemplate.ID); err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
} else {
|
||||
response.OkWithData(gin.H{"resysExportTemplate": resysExportTemplate}, c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSysExportTemplateList 分页获取导出模板列表
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 分页获取导出模板列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query systemReq.SysExportTemplateSearch true "分页获取导出模板列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysExportTemplate/getSysExportTemplateList [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) GetSysExportTemplateList(c *gin.Context) {
|
||||
var pageInfo systemReq.SysExportTemplateSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if list, total, err := sysExportTemplateService.GetSysExportTemplateInfoList(pageInfo); err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// ExportExcel 导出表格token
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 导出表格
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/exportExcel [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) ExportExcel(c *gin.Context) {
|
||||
templateID := c.Query("templateID")
|
||||
if templateID == "" {
|
||||
response.FailWithMessage("模板ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
queryParams := c.Request.URL.Query()
|
||||
|
||||
//创造一次性token
|
||||
token := utils.RandomString(32) // 随机32位
|
||||
|
||||
// 记录本次请求参数
|
||||
exportParams := map[string]interface{}{
|
||||
"templateID": templateID,
|
||||
"queryParams": queryParams,
|
||||
}
|
||||
|
||||
// 参数保留记录完成鉴权
|
||||
tokenMutex.Lock()
|
||||
exportTokenCache[token] = exportParams
|
||||
exportTokenExpiration[token] = time.Now().Add(30 * time.Minute)
|
||||
tokenMutex.Unlock()
|
||||
|
||||
// 生成一次性链接
|
||||
exportUrl := fmt.Sprintf("/sysExportTemplate/exportExcelByToken?token=%s", token)
|
||||
response.OkWithData(exportUrl, c)
|
||||
}
|
||||
|
||||
// ExportExcelByToken 导出表格
|
||||
// @Tags ExportExcelByToken
|
||||
// @Summary 导出表格
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/exportExcelByToken [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) ExportExcelByToken(c *gin.Context) {
|
||||
token := c.Query("token")
|
||||
if token == "" {
|
||||
response.FailWithMessage("导出token不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取token并且从缓存中剔除
|
||||
tokenMutex.RLock()
|
||||
exportParamsRaw, exists := exportTokenCache[token]
|
||||
expiry, _ := exportTokenExpiration[token]
|
||||
tokenMutex.RUnlock()
|
||||
|
||||
if !exists || time.Now().After(expiry) {
|
||||
global.GVA_LOG.Error("导出token无效或已过期!")
|
||||
response.FailWithMessage("导出token无效或已过期", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 从token获取参数
|
||||
exportParams, ok := exportParamsRaw.(map[string]interface{})
|
||||
if !ok {
|
||||
global.GVA_LOG.Error("解析导出参数失败!")
|
||||
response.FailWithMessage("解析导出参数失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取导出参数
|
||||
templateID := exportParams["templateID"].(string)
|
||||
queryParams := exportParams["queryParams"].(url.Values)
|
||||
|
||||
// 清理一次性token
|
||||
tokenMutex.Lock()
|
||||
delete(exportTokenCache, token)
|
||||
delete(exportTokenExpiration, token)
|
||||
tokenMutex.Unlock()
|
||||
|
||||
// 导出
|
||||
if file, name, err := sysExportTemplateService.ExportExcel(templateID, queryParams); err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+utils.RandomString(6)+".xlsx"))
|
||||
c.Header("success", "true")
|
||||
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// ExportTemplate 导出表格模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 导出表格模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/exportTemplate [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) ExportTemplate(c *gin.Context) {
|
||||
templateID := c.Query("templateID")
|
||||
if templateID == "" {
|
||||
response.FailWithMessage("模板ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 创造一次性token
|
||||
token := utils.RandomString(32) // 随机32位
|
||||
|
||||
// 记录本次请求参数
|
||||
exportParams := map[string]interface{}{
|
||||
"templateID": templateID,
|
||||
"isTemplate": true,
|
||||
}
|
||||
|
||||
// 参数保留记录完成鉴权
|
||||
tokenMutex.Lock()
|
||||
exportTokenCache[token] = exportParams
|
||||
exportTokenExpiration[token] = time.Now().Add(30 * time.Minute)
|
||||
tokenMutex.Unlock()
|
||||
|
||||
// 生成一次性链接
|
||||
exportUrl := fmt.Sprintf("/sysExportTemplate/exportTemplateByToken?token=%s", token)
|
||||
response.OkWithData(exportUrl, c)
|
||||
}
|
||||
|
||||
// ExportTemplateByToken 通过token导出表格模板
|
||||
// @Tags ExportTemplateByToken
|
||||
// @Summary 通过token导出表格模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/exportTemplateByToken [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) ExportTemplateByToken(c *gin.Context) {
|
||||
token := c.Query("token")
|
||||
if token == "" {
|
||||
response.FailWithMessage("导出token不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取token并且从缓存中剔除
|
||||
tokenMutex.RLock()
|
||||
exportParamsRaw, exists := exportTokenCache[token]
|
||||
expiry, _ := exportTokenExpiration[token]
|
||||
tokenMutex.RUnlock()
|
||||
|
||||
if !exists || time.Now().After(expiry) {
|
||||
global.GVA_LOG.Error("导出token无效或已过期!")
|
||||
response.FailWithMessage("导出token无效或已过期", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 从token获取参数
|
||||
exportParams, ok := exportParamsRaw.(map[string]interface{})
|
||||
if !ok {
|
||||
global.GVA_LOG.Error("解析导出参数失败!")
|
||||
response.FailWithMessage("解析导出参数失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否为模板导出
|
||||
isTemplate, _ := exportParams["isTemplate"].(bool)
|
||||
if !isTemplate {
|
||||
global.GVA_LOG.Error("token类型错误!")
|
||||
response.FailWithMessage("token类型错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取导出参数
|
||||
templateID := exportParams["templateID"].(string)
|
||||
|
||||
// 清理一次性token
|
||||
tokenMutex.Lock()
|
||||
delete(exportTokenCache, token)
|
||||
delete(exportTokenExpiration, token)
|
||||
tokenMutex.Unlock()
|
||||
|
||||
// 导出模板
|
||||
if file, name, err := sysExportTemplateService.ExportTemplate(templateID); err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+"模板.xlsx"))
|
||||
c.Header("success", "true")
|
||||
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// ImportExcel 导入表格
|
||||
// @Tags SysImportTemplate
|
||||
// @Summary 导入表格
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/importExcel [post]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) ImportExcel(c *gin.Context) {
|
||||
templateID := c.Query("templateID")
|
||||
if templateID == "" {
|
||||
response.FailWithMessage("模板ID不能为空", c)
|
||||
return
|
||||
}
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("文件获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("文件获取失败", c)
|
||||
return
|
||||
}
|
||||
if err := sysExportTemplateService.ImportExcel(templateID, file); err != nil {
|
||||
global.GVA_LOG.Error(err.Error(), zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
} else {
|
||||
response.OkWithMessage("导入成功", c)
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,12 @@ import (
|
||||
type DBApi struct{}
|
||||
|
||||
// InitDB
|
||||
// @Tags InitDB
|
||||
// @Summary 初始化用户数据库
|
||||
// @Tags InitDB
|
||||
// @Summary 初始化用户数据库
|
||||
// @Produce application/json
|
||||
// @Param data body request.InitDB true "初始化数据库参数"
|
||||
// @Success 200 {string} string "{"code":0,"data":{},"msg":"自动创建数据库成功"}"
|
||||
// @Router /init/initdb [post]
|
||||
// @Param data body request.InitDB true "初始化数据库参数"
|
||||
// @Success 200 {object} response.Response{data=string} "初始化用户数据库"
|
||||
// @Router /init/initdb [post]
|
||||
func (i *DBApi) InitDB(c *gin.Context) {
|
||||
if global.GVA_DB != nil {
|
||||
global.GVA_LOG.Error("已存在数据库配置!")
|
||||
@@ -35,23 +35,25 @@ func (i *DBApi) InitDB(c *gin.Context) {
|
||||
response.FailWithMessage("自动创建数据库失败,请查看后台日志,检查后在进行初始化", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData("自动创建数据库成功", c)
|
||||
response.OkWithMessage("自动创建数据库成功", c)
|
||||
}
|
||||
|
||||
// CheckDB
|
||||
// @Tags CheckDB
|
||||
// @Summary 初始化用户数据库
|
||||
// @Tags CheckDB
|
||||
// @Summary 初始化用户数据库
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"code":0,"data":{},"msg":"探测完成"}"
|
||||
// @Router /init/checkdb [post]
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "初始化用户数据库"
|
||||
// @Router /init/checkdb [post]
|
||||
func (i *DBApi) CheckDB(c *gin.Context) {
|
||||
var (
|
||||
message = "前往初始化数据库"
|
||||
needInit = true
|
||||
)
|
||||
|
||||
if global.GVA_DB != nil {
|
||||
global.GVA_LOG.Info("数据库无需初始化")
|
||||
response.OkWithDetailed(gin.H{"needInit": false}, "数据库无需初始化", c)
|
||||
return
|
||||
} else {
|
||||
global.GVA_LOG.Info("前往初始化数据库")
|
||||
response.OkWithDetailed(gin.H{"needInit": true}, "前往初始化数据库", c)
|
||||
return
|
||||
message = "数据库无需初始化"
|
||||
needInit = false
|
||||
}
|
||||
global.GVA_LOG.Info(message)
|
||||
response.OkWithDetailed(gin.H{"needInit": needInit}, message, c)
|
||||
}
|
||||
|
||||
@@ -4,26 +4,30 @@ import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type JwtApi struct{}
|
||||
|
||||
// @Tags Jwt
|
||||
// @Summary jwt加入黑名单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"拉黑成功"}"
|
||||
// @Router /jwt/jsonInBlacklist [post]
|
||||
// JsonInBlacklist
|
||||
// @Tags Jwt
|
||||
// @Summary jwt加入黑名单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "jwt加入黑名单"
|
||||
// @Router /jwt/jsonInBlacklist [post]
|
||||
func (j *JwtApi) JsonInBlacklist(c *gin.Context) {
|
||||
token := c.Request.Header.Get("x-token")
|
||||
token := utils.GetToken(c)
|
||||
jwt := system.JwtBlacklist{Jwt: token}
|
||||
if err := jwtService.JsonInBlacklist(jwt); err != nil {
|
||||
err := jwtService.JsonInBlacklist(jwt)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("jwt作废失败!", zap.Error(err))
|
||||
response.FailWithMessage("jwt作废失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("jwt作废成功", c)
|
||||
return
|
||||
}
|
||||
utils.ClearToken(c)
|
||||
response.OkWithMessage("jwt作废成功", c)
|
||||
}
|
||||
|
||||
+165
-126
@@ -15,57 +15,68 @@ import (
|
||||
|
||||
type AuthorityMenuApi struct{}
|
||||
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 获取用户动态路由
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body request.Empty true "空"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /menu/getMenu [post]
|
||||
// GetMenu
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 获取用户动态路由
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body request.Empty true "空"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单详情列表"
|
||||
// @Router /menu/getMenu [post]
|
||||
func (a *AuthorityMenuApi) GetMenu(c *gin.Context) {
|
||||
if err, menus := menuService.GetMenuTree(utils.GetUserAuthorityId(c)); err != nil {
|
||||
menus, err := menuService.GetMenuTree(utils.GetUserAuthorityId(c))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
if menus == nil {
|
||||
menus = []system.SysMenu{}
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
if menus == nil {
|
||||
menus = []system.SysMenu{}
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取成功", c)
|
||||
}
|
||||
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 获取用户动态路由
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body request.Empty true "空"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /menu/getBaseMenuTree [post]
|
||||
// GetBaseMenuTree
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 获取用户动态路由
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body request.Empty true "空"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysBaseMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单列表"
|
||||
// @Router /menu/getBaseMenuTree [post]
|
||||
func (a *AuthorityMenuApi) GetBaseMenuTree(c *gin.Context) {
|
||||
if err, menus := menuService.GetBaseMenuTree(); err != nil {
|
||||
authority := utils.GetUserAuthorityId(c)
|
||||
menus, err := menuService.GetBaseMenuTree(authority)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(systemRes.SysBaseMenusResponse{Menus: menus}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysBaseMenusResponse{Menus: menus}, "获取成功", c)
|
||||
}
|
||||
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 增加menu和角色关联关系
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.AddMenuAuthorityInfo true "角色ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"添加成功"}"
|
||||
// @Router /menu/addMenuAuthority [post]
|
||||
// AddMenuAuthority
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 增加menu和角色关联关系
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.AddMenuAuthorityInfo true "角色ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "增加menu和角色关联关系"
|
||||
// @Router /menu/addMenuAuthority [post]
|
||||
func (a *AuthorityMenuApi) AddMenuAuthority(c *gin.Context) {
|
||||
var authorityMenu systemReq.AddMenuAuthorityInfo
|
||||
_ = c.ShouldBindJSON(&authorityMenu)
|
||||
err := c.ShouldBindJSON(&authorityMenu)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := utils.Verify(authorityMenu, utils.AuthorityIdVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := menuService.AddMenuAuthority(authorityMenu.Menus, authorityMenu.AuthorityId); err != nil {
|
||||
adminAuthorityID := utils.GetUserAuthorityId(c)
|
||||
if err := menuService.AddMenuAuthority(authorityMenu.Menus, adminAuthorityID, authorityMenu.AuthorityId); err != nil {
|
||||
global.GVA_LOG.Error("添加失败!", zap.Error(err))
|
||||
response.FailWithMessage("添加失败", c)
|
||||
} else {
|
||||
@@ -73,154 +84,182 @@ func (a *AuthorityMenuApi) AddMenuAuthority(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 获取指定角色menu
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetAuthorityId true "角色ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /menu/getMenuAuthority [post]
|
||||
// GetMenuAuthority
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 获取指定角色menu
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetAuthorityId true "角色ID"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取指定角色menu"
|
||||
// @Router /menu/getMenuAuthority [post]
|
||||
func (a *AuthorityMenuApi) GetMenuAuthority(c *gin.Context) {
|
||||
var param request.GetAuthorityId
|
||||
_ = c.ShouldBindJSON(¶m)
|
||||
if err := utils.Verify(param, utils.AuthorityIdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(¶m)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, menus := menuService.GetMenuAuthority(¶m); err != nil {
|
||||
err = utils.Verify(param, utils.AuthorityIdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
menus, err := menuService.GetMenuAuthority(¶m)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"menus": menus}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"menus": menus}, "获取成功", c)
|
||||
}
|
||||
|
||||
// @Tags Menu
|
||||
// @Summary 新增菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"添加成功"}"
|
||||
// @Router /menu/addBaseMenu [post]
|
||||
// AddBaseMenu
|
||||
// @Tags Menu
|
||||
// @Summary 新增菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
|
||||
// @Success 200 {object} response.Response{msg=string} "新增菜单"
|
||||
// @Router /menu/addBaseMenu [post]
|
||||
func (a *AuthorityMenuApi) AddBaseMenu(c *gin.Context) {
|
||||
var menu system.SysBaseMenu
|
||||
_ = c.ShouldBindJSON(&menu)
|
||||
if err := utils.Verify(menu, utils.MenuVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&menu)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := utils.Verify(menu.Meta, utils.MenuMetaVerify); err != nil {
|
||||
err = utils.Verify(menu, utils.MenuVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := menuService.AddBaseMenu(menu); err != nil {
|
||||
err = utils.Verify(menu.Meta, utils.MenuMetaVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = menuService.AddBaseMenu(menu)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("添加失败!", zap.Error(err))
|
||||
|
||||
response.FailWithMessage("添加失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("添加成功", c)
|
||||
response.FailWithMessage("添加失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("添加成功", c)
|
||||
}
|
||||
|
||||
// @Tags Menu
|
||||
// @Summary 删除菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "菜单id"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /menu/deleteBaseMenu [post]
|
||||
// DeleteBaseMenu
|
||||
// @Tags Menu
|
||||
// @Summary 删除菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "菜单id"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除菜单"
|
||||
// @Router /menu/deleteBaseMenu [post]
|
||||
func (a *AuthorityMenuApi) DeleteBaseMenu(c *gin.Context) {
|
||||
var menu request.GetById
|
||||
_ = c.ShouldBindJSON(&menu)
|
||||
if err := utils.Verify(menu, utils.IdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&menu)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := baseMenuService.DeleteBaseMenu(menu.ID); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
err = utils.Verify(menu, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = baseMenuService.DeleteBaseMenu(menu.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// @Tags Menu
|
||||
// @Summary 更新菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /menu/updateBaseMenu [post]
|
||||
// UpdateBaseMenu
|
||||
// @Tags Menu
|
||||
// @Summary 更新菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新菜单"
|
||||
// @Router /menu/updateBaseMenu [post]
|
||||
func (a *AuthorityMenuApi) UpdateBaseMenu(c *gin.Context) {
|
||||
var menu system.SysBaseMenu
|
||||
_ = c.ShouldBindJSON(&menu)
|
||||
if err := utils.Verify(menu, utils.MenuVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&menu)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := utils.Verify(menu.Meta, utils.MenuMetaVerify); err != nil {
|
||||
err = utils.Verify(menu, utils.MenuVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := baseMenuService.UpdateBaseMenu(menu); err != nil {
|
||||
err = utils.Verify(menu.Meta, utils.MenuMetaVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = baseMenuService.UpdateBaseMenu(menu)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// @Tags Menu
|
||||
// @Summary 根据id获取菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "菜单id"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /menu/getBaseMenuById [post]
|
||||
// GetBaseMenuById
|
||||
// @Tags Menu
|
||||
// @Summary 根据id获取菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "菜单id"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysBaseMenuResponse,msg=string} "根据id获取菜单,返回包括系统菜单列表"
|
||||
// @Router /menu/getBaseMenuById [post]
|
||||
func (a *AuthorityMenuApi) GetBaseMenuById(c *gin.Context) {
|
||||
var idInfo request.GetById
|
||||
_ = c.ShouldBindJSON(&idInfo)
|
||||
if err := utils.Verify(idInfo, utils.IdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&idInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, menu := baseMenuService.GetBaseMenuById(idInfo.ID); err != nil {
|
||||
err = utils.Verify(idInfo, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
menu, err := baseMenuService.GetBaseMenuById(idInfo.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(systemRes.SysBaseMenuResponse{Menu: menu}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysBaseMenuResponse{Menu: menu}, "获取成功", c)
|
||||
}
|
||||
|
||||
// @Tags Menu
|
||||
// @Summary 分页获取基础menu列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /menu/getMenuList [post]
|
||||
// GetMenuList
|
||||
// @Tags Menu
|
||||
// @Summary 分页获取基础menu列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取基础menu列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /menu/getMenuList [post]
|
||||
func (a *AuthorityMenuApi) GetMenuList(c *gin.Context) {
|
||||
var pageInfo request.PageInfo
|
||||
_ = c.ShouldBindJSON(&pageInfo)
|
||||
if err := utils.Verify(pageInfo, utils.PageInfoVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, menuList, total := menuService.GetInfoList(); err != nil {
|
||||
authorityID := utils.GetUserAuthorityId(c)
|
||||
menuList, err := menuService.GetInfoList(authorityID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: menuList,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(menuList, "获取成功", c)
|
||||
}
|
||||
|
||||
@@ -13,106 +13,112 @@ import (
|
||||
|
||||
type OperationRecordApi struct{}
|
||||
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 创建SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysOperationRecord true "创建SysOperationRecord"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysOperationRecord/createSysOperationRecord [post]
|
||||
func (s *OperationRecordApi) CreateSysOperationRecord(c *gin.Context) {
|
||||
var sysOperationRecord system.SysOperationRecord
|
||||
_ = c.ShouldBindJSON(&sysOperationRecord)
|
||||
if err := operationRecordService.CreateSysOperationRecord(sysOperationRecord); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 删除SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysOperationRecord true "SysOperationRecord模型"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysOperationRecord/deleteSysOperationRecord [delete]
|
||||
// DeleteSysOperationRecord
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 删除SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysOperationRecord true "SysOperationRecord模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除SysOperationRecord"
|
||||
// @Router /sysOperationRecord/deleteSysOperationRecord [delete]
|
||||
func (s *OperationRecordApi) DeleteSysOperationRecord(c *gin.Context) {
|
||||
var sysOperationRecord system.SysOperationRecord
|
||||
_ = c.ShouldBindJSON(&sysOperationRecord)
|
||||
if err := operationRecordService.DeleteSysOperationRecord(sysOperationRecord); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 批量删除SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除SysOperationRecord"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}"
|
||||
// @Router /sysOperationRecord/deleteSysOperationRecordByIds [delete]
|
||||
func (s *OperationRecordApi) DeleteSysOperationRecordByIds(c *gin.Context) {
|
||||
var IDS request.IdsReq
|
||||
_ = c.ShouldBindJSON(&IDS)
|
||||
if err := operationRecordService.DeleteSysOperationRecordByIds(IDS); err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 用id查询SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysOperationRecord true "Id"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /sysOperationRecord/findSysOperationRecord [get]
|
||||
func (s *OperationRecordApi) FindSysOperationRecord(c *gin.Context) {
|
||||
var sysOperationRecord system.SysOperationRecord
|
||||
_ = c.ShouldBindQuery(&sysOperationRecord)
|
||||
if err := utils.Verify(sysOperationRecord, utils.IdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&sysOperationRecord)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, resysOperationRecord := operationRecordService.GetSysOperationRecord(sysOperationRecord.ID); err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"resysOperationRecord": resysOperationRecord}, "查询成功", c)
|
||||
err = operationRecordService.DeleteSysOperationRecord(sysOperationRecord)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 分页获取SysOperationRecord列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.SysOperationRecordSearch true "页码, 每页大小, 搜索条件"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysOperationRecord/getSysOperationRecordList [get]
|
||||
// DeleteSysOperationRecordByIds
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 批量删除SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除SysOperationRecord"
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除SysOperationRecord"
|
||||
// @Router /sysOperationRecord/deleteSysOperationRecordByIds [delete]
|
||||
func (s *OperationRecordApi) DeleteSysOperationRecordByIds(c *gin.Context) {
|
||||
var IDS request.IdsReq
|
||||
err := c.ShouldBindJSON(&IDS)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = operationRecordService.DeleteSysOperationRecordByIds(IDS)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// FindSysOperationRecord
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 用id查询SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysOperationRecord true "Id"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysOperationRecord"
|
||||
// @Router /sysOperationRecord/findSysOperationRecord [get]
|
||||
func (s *OperationRecordApi) FindSysOperationRecord(c *gin.Context) {
|
||||
var sysOperationRecord system.SysOperationRecord
|
||||
err := c.ShouldBindQuery(&sysOperationRecord)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(sysOperationRecord, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
reSysOperationRecord, err := operationRecordService.GetSysOperationRecord(sysOperationRecord.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"reSysOperationRecord": reSysOperationRecord}, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetSysOperationRecordList
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 分页获取SysOperationRecord列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.SysOperationRecordSearch true "页码, 每页大小, 搜索条件"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysOperationRecord列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /sysOperationRecord/getSysOperationRecordList [get]
|
||||
func (s *OperationRecordApi) GetSysOperationRecordList(c *gin.Context) {
|
||||
var pageInfo systemReq.SysOperationRecordSearch
|
||||
_ = c.ShouldBindQuery(&pageInfo)
|
||||
if err, list, total := operationRecordService.GetSysOperationRecordInfoList(pageInfo); err != nil {
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := operationRecordService.GetSysOperationRecordInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SysParamsApi struct{}
|
||||
|
||||
// CreateSysParams 创建参数
|
||||
// @Tags SysParams
|
||||
// @Summary 创建参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysParams true "创建参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /sysParams/createSysParams [post]
|
||||
func (sysParamsApi *SysParamsApi) CreateSysParams(c *gin.Context) {
|
||||
var sysParams system.SysParams
|
||||
err := c.ShouldBindJSON(&sysParams)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = sysParamsService.CreateSysParams(&sysParams)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysParams 删除参数
|
||||
// @Tags SysParams
|
||||
// @Summary 删除参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysParams true "删除参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /sysParams/deleteSysParams [delete]
|
||||
func (sysParamsApi *SysParamsApi) DeleteSysParams(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := sysParamsService.DeleteSysParams(ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysParamsByIds 批量删除参数
|
||||
// @Tags SysParams
|
||||
// @Summary 批量删除参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /sysParams/deleteSysParamsByIds [delete]
|
||||
func (sysParamsApi *SysParamsApi) DeleteSysParamsByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := sysParamsService.DeleteSysParamsByIds(IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateSysParams 更新参数
|
||||
// @Tags SysParams
|
||||
// @Summary 更新参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysParams true "更新参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /sysParams/updateSysParams [put]
|
||||
func (sysParamsApi *SysParamsApi) UpdateSysParams(c *gin.Context) {
|
||||
var sysParams system.SysParams
|
||||
err := c.ShouldBindJSON(&sysParams)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = sysParamsService.UpdateSysParams(sysParams)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindSysParams 用id查询参数
|
||||
// @Tags SysParams
|
||||
// @Summary 用id查询参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysParams true "用id查询参数"
|
||||
// @Success 200 {object} response.Response{data=system.SysParams,msg=string} "查询成功"
|
||||
// @Router /sysParams/findSysParams [get]
|
||||
func (sysParamsApi *SysParamsApi) FindSysParams(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
resysParams, err := sysParamsService.GetSysParams(ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(resysParams, c)
|
||||
}
|
||||
|
||||
// GetSysParamsList 分页获取参数列表
|
||||
// @Tags SysParams
|
||||
// @Summary 分页获取参数列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query systemReq.SysParamsSearch true "分页获取参数列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /sysParams/getSysParamsList [get]
|
||||
func (sysParamsApi *SysParamsApi) GetSysParamsList(c *gin.Context) {
|
||||
var pageInfo systemReq.SysParamsSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := sysParamsService.GetSysParamsInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetSysParam 根据key获取参数value
|
||||
// @Tags SysParams
|
||||
// @Summary 根据key获取参数value
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param key query string true "key"
|
||||
// @Success 200 {object} response.Response{data=system.SysParams,msg=string} "获取成功"
|
||||
// @Router /sysParams/getSysParam [get]
|
||||
func (sysParamsApi *SysParamsApi) GetSysParam(c *gin.Context) {
|
||||
k := c.Query("key")
|
||||
params, err := sysParamsService.GetSysParam(k)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(params, "获取成功", c)
|
||||
}
|
||||
@@ -6,73 +6,84 @@ import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SystemApi struct{}
|
||||
|
||||
// @Tags System
|
||||
// @Summary 获取配置文件内容
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /system/getSystemConfig [post]
|
||||
// GetSystemConfig
|
||||
// @Tags System
|
||||
// @Summary 获取配置文件内容
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysConfigResponse,msg=string} "获取配置文件内容,返回包括系统配置"
|
||||
// @Router /system/getSystemConfig [post]
|
||||
func (s *SystemApi) GetSystemConfig(c *gin.Context) {
|
||||
if err, config := systemConfigService.GetSystemConfig(); err != nil {
|
||||
config, err := systemConfigService.GetSystemConfig()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(systemRes.SysConfigResponse{Config: config}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysConfigResponse{Config: config}, "获取成功", c)
|
||||
}
|
||||
|
||||
// @Tags System
|
||||
// @Summary 设置配置文件内容
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body system.System true "设置配置文件内容"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
|
||||
// @Router /system/setSystemConfig [post]
|
||||
// SetSystemConfig
|
||||
// @Tags System
|
||||
// @Summary 设置配置文件内容
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body system.System true "设置配置文件内容"
|
||||
// @Success 200 {object} response.Response{data=string} "设置配置文件内容"
|
||||
// @Router /system/setSystemConfig [post]
|
||||
func (s *SystemApi) SetSystemConfig(c *gin.Context) {
|
||||
var sys system.System
|
||||
_ = c.ShouldBindJSON(&sys)
|
||||
if err := systemConfigService.SetSystemConfig(sys); err != nil {
|
||||
err := c.ShouldBindJSON(&sys)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = systemConfigService.SetSystemConfig(sys)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败", c)
|
||||
} else {
|
||||
response.OkWithData("设置成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
// @Tags System
|
||||
// @Summary 重启系统
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"code":0,"data":{},"msg":"重启系统成功"}"
|
||||
// @Router /system/reloadSystem [post]
|
||||
// ReloadSystem
|
||||
// @Tags System
|
||||
// @Summary 重载系统
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "重载系统"
|
||||
// @Router /system/reloadSystem [post]
|
||||
func (s *SystemApi) ReloadSystem(c *gin.Context) {
|
||||
err := utils.Reload()
|
||||
// 触发系统重载事件
|
||||
err := utils.GlobalSystemEvents.TriggerReload()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("重启系统失败!", zap.Error(err))
|
||||
response.FailWithMessage("重启系统失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("重启系统成功", c)
|
||||
global.GVA_LOG.Error("重载系统失败!", zap.Error(err))
|
||||
response.FailWithMessage("重载系统失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("重载系统成功", c)
|
||||
}
|
||||
|
||||
// @Tags System
|
||||
// @Summary 获取服务器信息
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /system/getServerInfo [post]
|
||||
// GetServerInfo
|
||||
// @Tags System
|
||||
// @Summary 获取服务器信息
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取服务器信息"
|
||||
// @Router /system/getServerInfo [post]
|
||||
func (s *SystemApi) GetServerInfo(c *gin.Context) {
|
||||
if server, err := systemConfigService.GetServerInfo(); err != nil {
|
||||
server, err := systemConfigService.GetServerInfo()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"server": server}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"server": server}, "获取成功", c)
|
||||
}
|
||||
|
||||
+326
-166
@@ -2,81 +2,106 @@ package system
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// @Tags Base
|
||||
// @Summary 用户登录
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.Login true "用户名, 密码, 验证码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"登陆成功"}"
|
||||
// @Router /base/login [post]
|
||||
// Login
|
||||
// @Tags Base
|
||||
// @Summary 用户登录
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.Login true "用户名, 密码, 验证码"
|
||||
// @Success 200 {object} response.Response{data=systemRes.LoginResponse,msg=string} "返回包括用户信息,token,过期时间"
|
||||
// @Router /base/login [post]
|
||||
func (b *BaseApi) Login(c *gin.Context) {
|
||||
var l systemReq.Login
|
||||
_ = c.ShouldBindJSON(&l)
|
||||
if err := utils.Verify(l, utils.LoginVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&l)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if store.Verify(l.CaptchaId, l.Captcha, true) {
|
||||
u := &system.SysUser{Username: l.Username, Password: l.Password}
|
||||
if err, user := userService.Login(u); err != nil {
|
||||
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
|
||||
response.FailWithMessage("用户名不存在或者密码错误", c)
|
||||
} else {
|
||||
b.tokenNext(c, *user)
|
||||
}
|
||||
} else {
|
||||
response.FailWithMessage("验证码错误", c)
|
||||
err = utils.Verify(l, utils.LoginVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
key := c.ClientIP()
|
||||
// 判断验证码是否开启
|
||||
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
|
||||
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
|
||||
v, ok := global.BlackCache.Get(key)
|
||||
if !ok {
|
||||
global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
|
||||
}
|
||||
|
||||
var oc bool = openCaptcha == 0 || openCaptcha < interfaceToInt(v)
|
||||
if oc && (l.Captcha == "" || l.CaptchaId == "" || !store.Verify(l.CaptchaId, l.Captcha, true)) {
|
||||
// 验证码次数+1
|
||||
global.BlackCache.Increment(key, 1)
|
||||
response.FailWithMessage("验证码错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
u := &system.SysUser{Username: l.Username, Password: l.Password}
|
||||
user, err := userService.Login(u)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
|
||||
// 验证码次数+1
|
||||
global.BlackCache.Increment(key, 1)
|
||||
response.FailWithMessage("用户名不存在或者密码错误", c)
|
||||
return
|
||||
}
|
||||
if user.Enable != 1 {
|
||||
global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
|
||||
// 验证码次数+1
|
||||
global.BlackCache.Increment(key, 1)
|
||||
response.FailWithMessage("用户被禁止登录", c)
|
||||
return
|
||||
}
|
||||
b.TokenNext(c, *user)
|
||||
}
|
||||
|
||||
// 登录以后签发jwt
|
||||
func (b *BaseApi) tokenNext(c *gin.Context, user system.SysUser) {
|
||||
j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名
|
||||
claims := j.CreateClaims(systemReq.BaseClaims{
|
||||
UUID: user.UUID,
|
||||
ID: user.ID,
|
||||
NickName: user.NickName,
|
||||
Username: user.Username,
|
||||
AuthorityId: user.AuthorityId,
|
||||
})
|
||||
token, err := j.CreateToken(claims)
|
||||
// TokenNext 登录以后签发jwt
|
||||
func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
|
||||
token, claims, err := utils.LoginToken(&user)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取token失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取token失败", c)
|
||||
return
|
||||
}
|
||||
if !global.GVA_CONFIG.System.UseMultipoint {
|
||||
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
|
||||
response.OkWithDetailed(systemRes.LoginResponse{
|
||||
User: user,
|
||||
Token: token,
|
||||
ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
|
||||
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
|
||||
}, "登录成功", c)
|
||||
return
|
||||
}
|
||||
|
||||
if err, jwtStr := jwtService.GetRedisJWT(user.Username); err == redis.Nil {
|
||||
if err := jwtService.SetRedisJWT(token, user.Username); err != nil {
|
||||
if jwtStr, err := jwtService.GetRedisJWT(user.Username); err == redis.Nil {
|
||||
if err := utils.SetRedisJWT(token, user.Username); err != nil {
|
||||
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置登录状态失败", c)
|
||||
return
|
||||
}
|
||||
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
|
||||
response.OkWithDetailed(systemRes.LoginResponse{
|
||||
User: user,
|
||||
Token: token,
|
||||
ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
|
||||
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
|
||||
}, "登录成功", c)
|
||||
} else if err != nil {
|
||||
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
|
||||
@@ -88,28 +113,35 @@ func (b *BaseApi) tokenNext(c *gin.Context, user system.SysUser) {
|
||||
response.FailWithMessage("jwt作废失败", c)
|
||||
return
|
||||
}
|
||||
if err := jwtService.SetRedisJWT(token, user.Username); err != nil {
|
||||
if err := utils.SetRedisJWT(token, user.GetUsername()); err != nil {
|
||||
response.FailWithMessage("设置登录状态失败", c)
|
||||
return
|
||||
}
|
||||
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
|
||||
response.OkWithDetailed(systemRes.LoginResponse{
|
||||
User: user,
|
||||
Token: token,
|
||||
ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
|
||||
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
|
||||
}, "登录成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// @Tags SysUser
|
||||
// @Summary 用户注册账号
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.Register true "用户名, 昵称, 密码, 角色ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"注册成功"}"
|
||||
// @Router /user/register [post]
|
||||
// Register
|
||||
// @Tags SysUser
|
||||
// @Summary 用户注册账号
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.Register true "用户名, 昵称, 密码, 角色ID"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysUserResponse,msg=string} "用户注册账号,返回包括用户信息"
|
||||
// @Router /user/admin_register [post]
|
||||
func (b *BaseApi) Register(c *gin.Context) {
|
||||
var r systemReq.Register
|
||||
_ = c.ShouldBindJSON(&r)
|
||||
if err := utils.Verify(r, utils.RegisterVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&r)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(r, utils.RegisterVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
@@ -119,204 +151,332 @@ func (b *BaseApi) Register(c *gin.Context) {
|
||||
AuthorityId: v,
|
||||
})
|
||||
}
|
||||
user := &system.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId, Authorities: authorities}
|
||||
err, userReturn := userService.Register(*user)
|
||||
user := &system.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId, Authorities: authorities, Enable: r.Enable, Phone: r.Phone, Email: r.Email}
|
||||
userReturn, err := userService.Register(*user)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("注册失败!", zap.Error(err))
|
||||
response.FailWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysUser
|
||||
// @Summary 用户修改密码
|
||||
// @Security ApiKeyAuth
|
||||
// ChangePassword
|
||||
// @Tags SysUser
|
||||
// @Summary 用户修改密码
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.ChangePasswordStruct true "用户名, 原密码, 新密码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
|
||||
// @Router /user/changePassword [post]
|
||||
// @Param data body systemReq.ChangePasswordReq true "用户名, 原密码, 新密码"
|
||||
// @Success 200 {object} response.Response{msg=string} "用户修改密码"
|
||||
// @Router /user/changePassword [post]
|
||||
func (b *BaseApi) ChangePassword(c *gin.Context) {
|
||||
var user systemReq.ChangePasswordStruct
|
||||
_ = c.ShouldBindJSON(&user)
|
||||
if err := utils.Verify(user, utils.ChangePasswordVerify); err != nil {
|
||||
var req systemReq.ChangePasswordReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
u := &system.SysUser{Username: user.Username, Password: user.Password}
|
||||
if err, _ := userService.ChangePassword(u, user.NewPassword); err != nil {
|
||||
err = utils.Verify(req, utils.ChangePasswordVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
uid := utils.GetUserID(c)
|
||||
u := &system.SysUser{GVA_MODEL: global.GVA_MODEL{ID: uid}, Password: req.Password}
|
||||
err = userService.ChangePassword(u, req.NewPassword)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage("修改失败,原密码与当前账户不符", c)
|
||||
} else {
|
||||
response.OkWithMessage("修改成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysUser
|
||||
// @Summary 分页获取用户列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /user/getUserList [post]
|
||||
// GetUserList
|
||||
// @Tags SysUser
|
||||
// @Summary 分页获取用户列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.GetUserList true "页码, 每页大小"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取用户列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /user/getUserList [post]
|
||||
func (b *BaseApi) GetUserList(c *gin.Context) {
|
||||
var pageInfo request.PageInfo
|
||||
_ = c.ShouldBindJSON(&pageInfo)
|
||||
if err := utils.Verify(pageInfo, utils.PageInfoVerify); err != nil {
|
||||
var pageInfo systemReq.GetUserList
|
||||
err := c.ShouldBindJSON(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, list, total := userService.GetUserInfoList(pageInfo); err != nil {
|
||||
err = utils.Verify(pageInfo, utils.PageInfoVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := userService.GetUserInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysUser
|
||||
// @Summary 更改用户权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SetUserAuth true "用户UUID, 角色ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
|
||||
// @Router /user/setUserAuthority [post]
|
||||
// SetUserAuthority
|
||||
// @Tags SysUser
|
||||
// @Summary 更改用户权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SetUserAuth true "用户UUID, 角色ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "设置用户权限"
|
||||
// @Router /user/setUserAuthority [post]
|
||||
func (b *BaseApi) SetUserAuthority(c *gin.Context) {
|
||||
var sua systemReq.SetUserAuth
|
||||
_ = c.ShouldBindJSON(&sua)
|
||||
err := c.ShouldBindJSON(&sua)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if UserVerifyErr := utils.Verify(sua, utils.SetUserAuthorityVerify); UserVerifyErr != nil {
|
||||
response.FailWithMessage(UserVerifyErr.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
uuid := utils.GetUserUuid(c)
|
||||
if err := userService.SetUserAuthority(userID, uuid, sua.AuthorityId); err != nil {
|
||||
err = userService.SetUserAuthority(userID, sua.AuthorityId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
} else {
|
||||
claims := utils.GetUserInfo(c)
|
||||
j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名
|
||||
claims.AuthorityId = sua.AuthorityId
|
||||
if token, err := j.CreateToken(*claims); err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
} else {
|
||||
c.Header("new-token", token)
|
||||
c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt, 10))
|
||||
response.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
claims := utils.GetUserInfo(c)
|
||||
claims.AuthorityId = sua.AuthorityId
|
||||
token, err := utils.NewJWT().CreateToken(*claims)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
c.Header("new-token", token)
|
||||
c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt.Unix(), 10))
|
||||
utils.SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60))
|
||||
response.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysUser
|
||||
// @Summary 设置用户权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SetUserAuthorities true "用户UUID, 角色ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
|
||||
// @Router /user/setUserAuthorities [post]
|
||||
// SetUserAuthorities
|
||||
// @Tags SysUser
|
||||
// @Summary 设置用户权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SetUserAuthorities true "用户UUID, 角色ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "设置用户权限"
|
||||
// @Router /user/setUserAuthorities [post]
|
||||
func (b *BaseApi) SetUserAuthorities(c *gin.Context) {
|
||||
var sua systemReq.SetUserAuthorities
|
||||
_ = c.ShouldBindJSON(&sua)
|
||||
if err := userService.SetUserAuthorities(sua.ID, sua.AuthorityIds); err != nil {
|
||||
err := c.ShouldBindJSON(&sua)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
authorityID := utils.GetUserAuthorityId(c)
|
||||
err = userService.SetUserAuthorities(authorityID, sua.ID, sua.AuthorityIds)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage("修改失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("修改成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysUser
|
||||
// @Summary 删除用户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "用户ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /user/deleteUser [delete]
|
||||
// DeleteUser
|
||||
// @Tags SysUser
|
||||
// @Summary 删除用户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "用户ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除用户"
|
||||
// @Router /user/deleteUser [delete]
|
||||
func (b *BaseApi) DeleteUser(c *gin.Context) {
|
||||
var reqId request.GetById
|
||||
_ = c.ShouldBindJSON(&reqId)
|
||||
if err := utils.Verify(reqId, utils.IdVerify); err != nil {
|
||||
err := c.ShouldBindJSON(&reqId)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(reqId, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
jwtId := utils.GetUserID(c)
|
||||
if jwtId == uint(reqId.ID) {
|
||||
response.FailWithMessage("删除失败, 自杀失败", c)
|
||||
response.FailWithMessage("删除失败, 无法删除自己。", c)
|
||||
return
|
||||
}
|
||||
if err := userService.DeleteUser(reqId.ID); err != nil {
|
||||
err = userService.DeleteUser(reqId.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysUser
|
||||
// @Summary 设置用户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
|
||||
// @Router /user/setUserInfo [put]
|
||||
// SetUserInfo
|
||||
// @Tags SysUser
|
||||
// @Summary 设置用户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户信息"
|
||||
// @Router /user/setUserInfo [put]
|
||||
func (b *BaseApi) SetUserInfo(c *gin.Context) {
|
||||
var user system.SysUser
|
||||
_ = c.ShouldBindJSON(&user)
|
||||
if err := utils.Verify(user, utils.IdVerify); err != nil {
|
||||
var user systemReq.ChangeUserInfo
|
||||
err := c.ShouldBindJSON(&user)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err, ReqUser := userService.SetUserInfo(user); err != nil {
|
||||
err = utils.Verify(user, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if len(user.AuthorityIds) != 0 {
|
||||
authorityID := utils.GetUserAuthorityId(c)
|
||||
err = userService.SetUserAuthorities(authorityID, user.ID, user.AuthorityIds)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败", c)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = userService.SetUserInfo(system.SysUser{
|
||||
GVA_MODEL: global.GVA_MODEL{
|
||||
ID: user.ID,
|
||||
},
|
||||
NickName: user.NickName,
|
||||
HeaderImg: user.HeaderImg,
|
||||
Phone: user.Phone,
|
||||
Email: user.Email,
|
||||
Enable: user.Enable,
|
||||
})
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "设置成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysUser
|
||||
// @Summary 获取用户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /user/getUserInfo [get]
|
||||
// SetSelfInfo
|
||||
// @Tags SysUser
|
||||
// @Summary 设置用户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户信息"
|
||||
// @Router /user/SetSelfInfo [put]
|
||||
func (b *BaseApi) SetSelfInfo(c *gin.Context) {
|
||||
var user systemReq.ChangeUserInfo
|
||||
err := c.ShouldBindJSON(&user)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
user.ID = utils.GetUserID(c)
|
||||
err = userService.SetSelfInfo(system.SysUser{
|
||||
GVA_MODEL: global.GVA_MODEL{
|
||||
ID: user.ID,
|
||||
},
|
||||
NickName: user.NickName,
|
||||
HeaderImg: user.HeaderImg,
|
||||
Phone: user.Phone,
|
||||
Email: user.Email,
|
||||
Enable: user.Enable,
|
||||
})
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
// SetSelfSetting
|
||||
// @Tags SysUser
|
||||
// @Summary 设置用户配置
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body map[string]interface{} true "用户配置数据"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户配置"
|
||||
// @Router /user/SetSelfSetting [put]
|
||||
func (b *BaseApi) SetSelfSetting(c *gin.Context) {
|
||||
var req common.JSONMap
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err = userService.SetSelfSetting(req, utils.GetUserID(c))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
// GetUserInfo
|
||||
// @Tags SysUser
|
||||
// @Summary 获取用户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取用户信息"
|
||||
// @Router /user/getUserInfo [get]
|
||||
func (b *BaseApi) GetUserInfo(c *gin.Context) {
|
||||
uuid := utils.GetUserUuid(c)
|
||||
if err, ReqUser := userService.GetUserInfo(uuid); err != nil {
|
||||
ReqUser, err := userService.GetUserInfo(uuid)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "获取成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "获取成功", c)
|
||||
}
|
||||
|
||||
// @Tags SysUser
|
||||
// @Summary 用户修改密码
|
||||
// @Security ApiKeyAuth
|
||||
// ResetPassword
|
||||
// @Tags SysUser
|
||||
// @Summary 重置用户密码
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysUser true "ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
|
||||
// @Router /user/resetPassword [post]
|
||||
// @Param data body system.SysUser true "ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "重置用户密码"
|
||||
// @Router /user/resetPassword [post]
|
||||
func (b *BaseApi) ResetPassword(c *gin.Context) {
|
||||
var user system.SysUser
|
||||
_ = c.ShouldBindJSON(&user)
|
||||
if err := userService.ResetPassword(user.ID); err != nil {
|
||||
var rps systemReq.ResetPassword
|
||||
err := c.ShouldBindJSON(&rps)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = userService.ResetPassword(rps.ID, rps.Password)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("重置失败!", zap.Error(err))
|
||||
response.FailWithMessage("重置失败"+err.Error(), c)
|
||||
} else {
|
||||
response.OkWithMessage("重置成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("重置成功", c)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,486 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
||||
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SysVersionApi struct{}
|
||||
|
||||
// buildMenuTree 构建菜单树结构
|
||||
func buildMenuTree(menus []system.SysBaseMenu) []system.SysBaseMenu {
|
||||
// 创建菜单映射
|
||||
menuMap := make(map[uint]*system.SysBaseMenu)
|
||||
for i := range menus {
|
||||
menuMap[menus[i].ID] = &menus[i]
|
||||
}
|
||||
|
||||
// 构建树结构
|
||||
var rootMenus []system.SysBaseMenu
|
||||
for _, menu := range menus {
|
||||
if menu.ParentId == 0 {
|
||||
// 根菜单
|
||||
menuData := convertMenuToStruct(menu, menuMap)
|
||||
rootMenus = append(rootMenus, menuData)
|
||||
}
|
||||
}
|
||||
|
||||
// 按sort排序根菜单
|
||||
sort.Slice(rootMenus, func(i, j int) bool {
|
||||
return rootMenus[i].Sort < rootMenus[j].Sort
|
||||
})
|
||||
|
||||
return rootMenus
|
||||
}
|
||||
|
||||
// convertMenuToStruct 将菜单转换为结构体并递归处理子菜单
|
||||
func convertMenuToStruct(menu system.SysBaseMenu, menuMap map[uint]*system.SysBaseMenu) system.SysBaseMenu {
|
||||
result := system.SysBaseMenu{
|
||||
Path: menu.Path,
|
||||
Name: menu.Name,
|
||||
Hidden: menu.Hidden,
|
||||
Component: menu.Component,
|
||||
Sort: menu.Sort,
|
||||
Meta: menu.Meta,
|
||||
}
|
||||
|
||||
// 清理并复制参数数据
|
||||
if len(menu.Parameters) > 0 {
|
||||
cleanParameters := make([]system.SysBaseMenuParameter, 0, len(menu.Parameters))
|
||||
for _, param := range menu.Parameters {
|
||||
cleanParam := system.SysBaseMenuParameter{
|
||||
Type: param.Type,
|
||||
Key: param.Key,
|
||||
Value: param.Value,
|
||||
// 不复制 ID, CreatedAt, UpdatedAt, SysBaseMenuID
|
||||
}
|
||||
cleanParameters = append(cleanParameters, cleanParam)
|
||||
}
|
||||
result.Parameters = cleanParameters
|
||||
}
|
||||
|
||||
// 清理并复制菜单按钮数据
|
||||
if len(menu.MenuBtn) > 0 {
|
||||
cleanMenuBtns := make([]system.SysBaseMenuBtn, 0, len(menu.MenuBtn))
|
||||
for _, btn := range menu.MenuBtn {
|
||||
cleanBtn := system.SysBaseMenuBtn{
|
||||
Name: btn.Name,
|
||||
Desc: btn.Desc,
|
||||
// 不复制 ID, CreatedAt, UpdatedAt, SysBaseMenuID
|
||||
}
|
||||
cleanMenuBtns = append(cleanMenuBtns, cleanBtn)
|
||||
}
|
||||
result.MenuBtn = cleanMenuBtns
|
||||
}
|
||||
|
||||
// 查找并处理子菜单
|
||||
var children []system.SysBaseMenu
|
||||
for _, childMenu := range menuMap {
|
||||
if childMenu.ParentId == menu.ID {
|
||||
childData := convertMenuToStruct(*childMenu, menuMap)
|
||||
children = append(children, childData)
|
||||
}
|
||||
}
|
||||
|
||||
// 按sort排序子菜单
|
||||
if len(children) > 0 {
|
||||
sort.Slice(children, func(i, j int) bool {
|
||||
return children[i].Sort < children[j].Sort
|
||||
})
|
||||
result.Children = children
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// DeleteSysVersion 删除版本管理
|
||||
// @Tags SysVersion
|
||||
// @Summary 删除版本管理
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysVersion true "删除版本管理"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /sysVersion/deleteSysVersion [delete]
|
||||
func (sysVersionApi *SysVersionApi) DeleteSysVersion(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
ID := c.Query("ID")
|
||||
err := sysVersionService.DeleteSysVersion(ctx, ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysVersionByIds 批量删除版本管理
|
||||
// @Tags SysVersion
|
||||
// @Summary 批量删除版本管理
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /sysVersion/deleteSysVersionByIds [delete]
|
||||
func (sysVersionApi *SysVersionApi) DeleteSysVersionByIds(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := sysVersionService.DeleteSysVersionByIds(ctx, IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// FindSysVersion 用id查询版本管理
|
||||
// @Tags SysVersion
|
||||
// @Summary 用id查询版本管理
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param ID query uint true "用id查询版本管理"
|
||||
// @Success 200 {object} response.Response{data=system.SysVersion,msg=string} "查询成功"
|
||||
// @Router /sysVersion/findSysVersion [get]
|
||||
func (sysVersionApi *SysVersionApi) FindSysVersion(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
ID := c.Query("ID")
|
||||
resysVersion, err := sysVersionService.GetSysVersion(ctx, ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(resysVersion, c)
|
||||
}
|
||||
|
||||
// GetSysVersionList 分页获取版本管理列表
|
||||
// @Tags SysVersion
|
||||
// @Summary 分页获取版本管理列表
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query systemReq.SysVersionSearch true "分页获取版本管理列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /sysVersion/getSysVersionList [get]
|
||||
func (sysVersionApi *SysVersionApi) GetSysVersionList(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var pageInfo systemReq.SysVersionSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := sysVersionService.GetSysVersionInfoList(ctx, pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetSysVersionPublic 不需要鉴权的版本管理接口
|
||||
// @Tags SysVersion
|
||||
// @Summary 不需要鉴权的版本管理接口
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /sysVersion/getSysVersionPublic [get]
|
||||
func (sysVersionApi *SysVersionApi) GetSysVersionPublic(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 此接口不需要鉴权
|
||||
// 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑
|
||||
sysVersionService.GetSysVersionPublic(ctx)
|
||||
response.OkWithDetailed(gin.H{
|
||||
"info": "不需要鉴权的版本管理接口信息",
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// ExportVersion 创建发版数据
|
||||
// @Tags SysVersion
|
||||
// @Summary 创建发版数据
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.ExportVersionRequest true "创建发版数据"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /sysVersion/exportVersion [post]
|
||||
func (sysVersionApi *SysVersionApi) ExportVersion(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var req systemReq.ExportVersionRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取选中的菜单数据
|
||||
var menuData []system.SysBaseMenu
|
||||
if len(req.MenuIds) > 0 {
|
||||
menuData, err = sysVersionService.GetMenusByIds(ctx, req.MenuIds)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取菜单数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取菜单数据失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 获取选中的API数据
|
||||
var apiData []system.SysApi
|
||||
if len(req.ApiIds) > 0 {
|
||||
apiData, err = sysVersionService.GetApisByIds(ctx, req.ApiIds)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取API数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取API数据失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 获取选中的字典数据
|
||||
var dictData []system.SysDictionary
|
||||
if len(req.DictIds) > 0 {
|
||||
dictData, err = sysVersionService.GetDictionariesByIds(ctx, req.DictIds)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取字典数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取字典数据失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 处理菜单数据,构建递归的children结构
|
||||
processedMenus := buildMenuTree(menuData)
|
||||
|
||||
// 处理API数据,清除ID和时间戳字段
|
||||
processedApis := make([]system.SysApi, 0, len(apiData))
|
||||
for _, api := range apiData {
|
||||
cleanApi := system.SysApi{
|
||||
Path: api.Path,
|
||||
Description: api.Description,
|
||||
ApiGroup: api.ApiGroup,
|
||||
Method: api.Method,
|
||||
}
|
||||
processedApis = append(processedApis, cleanApi)
|
||||
}
|
||||
|
||||
// 处理字典数据,清除ID和时间戳字段,包含字典详情
|
||||
processedDicts := make([]system.SysDictionary, 0, len(dictData))
|
||||
for _, dict := range dictData {
|
||||
cleanDict := system.SysDictionary{
|
||||
Name: dict.Name,
|
||||
Type: dict.Type,
|
||||
Status: dict.Status,
|
||||
Desc: dict.Desc,
|
||||
}
|
||||
|
||||
// 处理字典详情数据,清除ID和时间戳字段
|
||||
cleanDetails := make([]system.SysDictionaryDetail, 0, len(dict.SysDictionaryDetails))
|
||||
for _, detail := range dict.SysDictionaryDetails {
|
||||
cleanDetail := system.SysDictionaryDetail{
|
||||
Label: detail.Label,
|
||||
Value: detail.Value,
|
||||
Extend: detail.Extend,
|
||||
Status: detail.Status,
|
||||
Sort: detail.Sort,
|
||||
// 不复制 ID, CreatedAt, UpdatedAt, SysDictionaryID
|
||||
}
|
||||
cleanDetails = append(cleanDetails, cleanDetail)
|
||||
}
|
||||
cleanDict.SysDictionaryDetails = cleanDetails
|
||||
|
||||
processedDicts = append(processedDicts, cleanDict)
|
||||
}
|
||||
|
||||
// 构建导出数据
|
||||
exportData := systemRes.ExportVersionResponse{
|
||||
Version: systemReq.VersionInfo{
|
||||
Name: req.VersionName,
|
||||
Code: req.VersionCode,
|
||||
Description: req.Description,
|
||||
ExportTime: time.Now().Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
Menus: processedMenus,
|
||||
Apis: processedApis,
|
||||
Dictionaries: processedDicts,
|
||||
}
|
||||
|
||||
// 转换为JSON
|
||||
jsonData, err := json.MarshalIndent(exportData, "", " ")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("JSON序列化失败!", zap.Error(err))
|
||||
response.FailWithMessage("JSON序列化失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 保存版本记录
|
||||
version := system.SysVersion{
|
||||
VersionName: utils.Pointer(req.VersionName),
|
||||
VersionCode: utils.Pointer(req.VersionCode),
|
||||
Description: utils.Pointer(req.Description),
|
||||
VersionData: utils.Pointer(string(jsonData)),
|
||||
}
|
||||
|
||||
err = sysVersionService.CreateSysVersion(ctx, &version)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("保存版本记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("保存版本记录失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("创建发版成功", c)
|
||||
}
|
||||
|
||||
// DownloadVersionJson 下载版本JSON数据
|
||||
// @Tags SysVersion
|
||||
// @Summary 下载版本JSON数据
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param ID query string true "版本ID"
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "下载成功"
|
||||
// @Router /sysVersion/downloadVersionJson [get]
|
||||
func (sysVersionApi *SysVersionApi) DownloadVersionJson(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
ID := c.Query("ID")
|
||||
if ID == "" {
|
||||
response.FailWithMessage("版本ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取版本记录
|
||||
version, err := sysVersionService.GetSysVersion(ctx, ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取版本记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取版本记录失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 构建JSON数据
|
||||
var jsonData []byte
|
||||
if version.VersionData != nil && *version.VersionData != "" {
|
||||
jsonData = []byte(*version.VersionData)
|
||||
} else {
|
||||
// 如果没有存储的JSON数据,构建一个基本的结构
|
||||
basicData := systemRes.ExportVersionResponse{
|
||||
Version: systemReq.VersionInfo{
|
||||
Name: *version.VersionName,
|
||||
Code: *version.VersionCode,
|
||||
Description: *version.Description,
|
||||
ExportTime: version.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
Menus: []system.SysBaseMenu{},
|
||||
Apis: []system.SysApi{},
|
||||
}
|
||||
jsonData, _ = json.MarshalIndent(basicData, "", " ")
|
||||
}
|
||||
|
||||
// 设置下载响应头
|
||||
filename := fmt.Sprintf("version_%s_%s.json", *version.VersionCode, time.Now().Format("20060102150405"))
|
||||
c.Header("Content-Type", "application/json")
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
|
||||
c.Header("Content-Length", strconv.Itoa(len(jsonData)))
|
||||
|
||||
c.Data(http.StatusOK, "application/json", jsonData)
|
||||
}
|
||||
|
||||
// ImportVersion 导入版本数据
|
||||
// @Tags SysVersion
|
||||
// @Summary 导入版本数据
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.ImportVersionRequest true "版本JSON数据"
|
||||
// @Success 200 {object} response.Response{msg=string} "导入成功"
|
||||
// @Router /sysVersion/importVersion [post]
|
||||
func (sysVersionApi *SysVersionApi) ImportVersion(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 获取JSON数据
|
||||
var importData systemReq.ImportVersionRequest
|
||||
err := c.ShouldBindJSON(&importData)
|
||||
if err != nil {
|
||||
response.FailWithMessage("解析JSON数据失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证数据格式
|
||||
if importData.VersionInfo.Name == "" || importData.VersionInfo.Code == "" {
|
||||
response.FailWithMessage("版本信息格式错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 导入菜单数据
|
||||
if len(importData.ExportMenu) > 0 {
|
||||
if err := sysVersionService.ImportMenus(ctx, importData.ExportMenu); err != nil {
|
||||
global.GVA_LOG.Error("导入菜单失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入菜单失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 导入API数据
|
||||
if len(importData.ExportApi) > 0 {
|
||||
if err := sysVersionService.ImportApis(importData.ExportApi); err != nil {
|
||||
global.GVA_LOG.Error("导入API失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入API失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 导入字典数据
|
||||
if len(importData.ExportDictionary) > 0 {
|
||||
if err := sysVersionService.ImportDictionaries(importData.ExportDictionary); err != nil {
|
||||
global.GVA_LOG.Error("导入字典失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入字典失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 创建导入记录
|
||||
jsonData, _ := json.Marshal(importData)
|
||||
version := system.SysVersion{
|
||||
VersionName: utils.Pointer(importData.VersionInfo.Name),
|
||||
VersionCode: utils.Pointer(fmt.Sprintf("%s_imported_%s", importData.VersionInfo.Code, time.Now().Format("20060102150405"))),
|
||||
Description: utils.Pointer(fmt.Sprintf("导入版本: %s", importData.VersionInfo.Description)),
|
||||
VersionData: utils.Pointer(string(jsonData)),
|
||||
}
|
||||
|
||||
err = sysVersionService.CreateSysVersion(ctx, &version)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("保存导入记录失败!", zap.Error(err))
|
||||
// 这里不返回错误,因为数据已经导入成功
|
||||
}
|
||||
|
||||
response.OkWithMessage("导入成功", c)
|
||||
}
|
||||
+237
-108
@@ -2,153 +2,282 @@
|
||||
|
||||
# jwt configuration
|
||||
jwt:
|
||||
signing-key: 'qmPlus'
|
||||
expires-time: 604800
|
||||
buffer-time: 86400
|
||||
|
||||
signing-key: qmPlus
|
||||
expires-time: 7d
|
||||
buffer-time: 1d
|
||||
issuer: qmPlus
|
||||
# zap logger configuration
|
||||
zap:
|
||||
level: 'info'
|
||||
format: 'console'
|
||||
prefix: '[github.com/flipped-aurora/gin-vue-admin/server]'
|
||||
director: 'log'
|
||||
link-name: 'latest_log'
|
||||
show-line: true
|
||||
encode-level: 'LowercaseColorLevelEncoder'
|
||||
stacktrace-key: 'stacktrace'
|
||||
log-in-console: true
|
||||
level: info
|
||||
format: console
|
||||
prefix: "[github.com/flipped-aurora/gin-vue-admin/server]"
|
||||
director: log
|
||||
show-line: true
|
||||
encode-level: LowercaseColorLevelEncoder
|
||||
stacktrace-key: stacktrace
|
||||
log-in-console: true
|
||||
retention-day: -1
|
||||
|
||||
# redis configuration
|
||||
redis:
|
||||
db: 0
|
||||
addr: '177.7.0.14:6379'
|
||||
password: ''
|
||||
#是否使用redis集群模式
|
||||
useCluster: false
|
||||
#使用集群模式addr和db默认无效
|
||||
addr: 177.7.0.14:6379
|
||||
password: ""
|
||||
db: 0
|
||||
clusterAddrs:
|
||||
- "177.7.0.14:7000"
|
||||
- "177.7.0.15:7001"
|
||||
- "177.7.0.13:7002"
|
||||
|
||||
# redis-list configuration
|
||||
redis-list:
|
||||
- name: cache # 数据库的名称,注意: name 需要在 redis-list 中唯一
|
||||
useCluster: false # 是否使用redis集群模式
|
||||
addr: 177.7.0.14:6379 # 使用集群模式addr和db默认无效
|
||||
password: ""
|
||||
db: 0
|
||||
clusterAddrs:
|
||||
- "177.7.0.14:7000"
|
||||
- "177.7.0.15:7001"
|
||||
- "177.7.0.13:7002"
|
||||
|
||||
# mongo configuration
|
||||
mongo:
|
||||
coll: ''
|
||||
options: ''
|
||||
database: ''
|
||||
username: ''
|
||||
password: ''
|
||||
auth-source: ''
|
||||
min-pool-size: 0
|
||||
max-pool-size: 100
|
||||
socket-timeout-ms: 0
|
||||
connect-timeout-ms: 0
|
||||
is-zap: false
|
||||
hosts:
|
||||
- host: ''
|
||||
port: ''
|
||||
|
||||
# email configuration
|
||||
email:
|
||||
to: 'xxx@qq.com'
|
||||
port: 465
|
||||
from: 'xxx@163.com'
|
||||
host: 'smtp.163.com'
|
||||
is-ssl: true
|
||||
secret: 'xxx'
|
||||
nickname: 'test'
|
||||
|
||||
# casbin configuration
|
||||
casbin:
|
||||
model-path: './resource/rbac_model.conf'
|
||||
to: xxx@qq.com
|
||||
port: 465
|
||||
from: xxx@163.com
|
||||
host: smtp.163.com
|
||||
is-ssl: true
|
||||
secret: xxx
|
||||
nickname: test
|
||||
|
||||
# system configuration
|
||||
system:
|
||||
env: 'public' # Change to "develop" to skip authentication for development mode
|
||||
addr: 8888
|
||||
db-type: 'mysql'
|
||||
oss-type: 'local' # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
|
||||
use-multipoint: false
|
||||
env: local # 修改为public可以关闭路由日志输出
|
||||
addr: 8888
|
||||
db-type: mysql
|
||||
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
|
||||
use-redis: false # 使用redis
|
||||
use-mongo: false # 使用mongo
|
||||
use-multipoint: false
|
||||
# IP限制次数 一个小时15000次
|
||||
iplimit-count: 15000
|
||||
# IP限制一个小时
|
||||
iplimit-time: 3600
|
||||
# 路由全局前缀
|
||||
router-prefix: ""
|
||||
# 严格角色模式 打开后权限将会存在上下级关系
|
||||
use-strict-auth: false
|
||||
|
||||
# captcha configuration
|
||||
captcha:
|
||||
key-long: 6
|
||||
img-width: 240
|
||||
img-height: 80
|
||||
key-long: 6
|
||||
img-width: 240
|
||||
img-height: 80
|
||||
open-captcha: 0 # 0代表一直开启,大于0代表限制次数
|
||||
open-captcha-timeout: 3600 # open-captcha大于0时才生效
|
||||
|
||||
# mysql connect configuration
|
||||
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
|
||||
mysql:
|
||||
path: ''
|
||||
port: ''
|
||||
config: ''
|
||||
db-name: ''
|
||||
username: ''
|
||||
password: ''
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
log-mode: ""
|
||||
log-zap: false
|
||||
path: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
log-mode: ""
|
||||
log-zap: false
|
||||
|
||||
# pgsql connect configuration
|
||||
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
|
||||
pgsql:
|
||||
path: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
log-mode: ""
|
||||
log-zap: false
|
||||
oracle:
|
||||
path: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
log-mode: ""
|
||||
log-zap: false
|
||||
mssql:
|
||||
path: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
log-mode: ""
|
||||
log-zap: false
|
||||
sqlite:
|
||||
path: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
log-mode: ""
|
||||
log-zap: false
|
||||
db-list:
|
||||
- disable: true # 是否禁用
|
||||
type: "" # 数据库的类型,目前支持mysql、pgsql、mssql、oracle
|
||||
alias-name: "" # 数据库的名称,注意: alias-name 需要在db-list中唯一
|
||||
path: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
log-mode: ""
|
||||
log-zap: false
|
||||
|
||||
# local configuration
|
||||
local:
|
||||
path: 'uploads/file'
|
||||
path: uploads/file
|
||||
store-path: uploads/file
|
||||
|
||||
# autocode configuration
|
||||
autocode:
|
||||
transfer-restart: true
|
||||
root: ""
|
||||
server: /server
|
||||
server-api: /api/v1
|
||||
server-initialize: /initialize
|
||||
server-model: /model
|
||||
server-request: /model/request/
|
||||
server-router: /router
|
||||
server-service: /service
|
||||
web: /web/src
|
||||
web-api: /api
|
||||
web-form: /view
|
||||
web-table: /view
|
||||
web: web/src
|
||||
root: "" # root 自动适配项目根目录, 请不要手动配置,他会在项目加载的时候识别出根路径
|
||||
server: server
|
||||
module: 'github.com/flipped-aurora/gin-vue-admin/server'
|
||||
ai-path: "" # AI服务路径
|
||||
|
||||
# qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址)
|
||||
qiniu:
|
||||
zone: 'ZoneHuaDong'
|
||||
bucket: ''
|
||||
img-path: ''
|
||||
use-https: false
|
||||
access-key: ''
|
||||
secret-key: ''
|
||||
use-cdn-domains: false
|
||||
zone: ZoneHuaDong
|
||||
bucket: ""
|
||||
img-path: ""
|
||||
use-https: false
|
||||
access-key: ""
|
||||
secret-key: ""
|
||||
use-cdn-domains: false
|
||||
|
||||
# minio oss configuration
|
||||
minio:
|
||||
endpoint: yourEndpoint
|
||||
access-key-id: yourAccessKeyId
|
||||
access-key-secret: yourAccessKeySecret
|
||||
bucket-name: yourBucketName
|
||||
use-ssl: false
|
||||
base-path: ""
|
||||
bucket-url: "http://host:9000/yourBucketName"
|
||||
|
||||
# aliyun oss configuration
|
||||
aliyun-oss:
|
||||
endpoint: 'yourEndpoint'
|
||||
access-key-id: 'yourAccessKeyId'
|
||||
access-key-secret: 'yourAccessKeySecret'
|
||||
bucket-name: 'yourBucketName'
|
||||
bucket-url: 'yourBucketUrl'
|
||||
endpoint: yourEndpoint
|
||||
access-key-id: yourAccessKeyId
|
||||
access-key-secret: yourAccessKeySecret
|
||||
bucket-name: yourBucketName
|
||||
bucket-url: yourBucketUrl
|
||||
base-path: yourBasePath
|
||||
|
||||
# tencent cos configuration
|
||||
tencent-cos:
|
||||
bucket: 'xxxxx-10005608'
|
||||
region: 'ap-shanghai'
|
||||
secret-id: 'xxxxxxxx'
|
||||
secret-key: 'xxxxxxxx'
|
||||
base-url: 'https://gin.vue.admin'
|
||||
path-prefix: 'github.com/flipped-aurora/gin-vue-admin/server'
|
||||
bucket: xxxxx-10005608
|
||||
region: ap-shanghai
|
||||
secret-id: your-secret-id
|
||||
secret-key: your-secret-key
|
||||
base-url: https://gin.vue.admin
|
||||
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
|
||||
|
||||
# aws s3 configuration (minio compatible)
|
||||
aws-s3:
|
||||
bucket: xxxxx-10005608
|
||||
region: ap-shanghai
|
||||
endpoint: ""
|
||||
s3-force-path-style: false
|
||||
disable-ssl: false
|
||||
secret-id: your-secret-id
|
||||
secret-key: your-secret-key
|
||||
base-url: https://gin.vue.admin
|
||||
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
|
||||
|
||||
# cloudflare r2 configuration
|
||||
cloudflare-r2:
|
||||
bucket: xxxx0bucket
|
||||
base-url: https://gin.vue.admin.com
|
||||
path: uploads
|
||||
account-id: xxx_account_id
|
||||
access-key-id: xxx_key_id
|
||||
secret-access-key: xxx_secret_key
|
||||
|
||||
# huawei obs configuration
|
||||
hua-wei-obs:
|
||||
path: 'you-path'
|
||||
bucket: 'you-bucket'
|
||||
endpoint: 'you-endpoint'
|
||||
access-key: 'you-access-key'
|
||||
secret-key: 'you-secret-key'
|
||||
path: you-path
|
||||
bucket: you-bucket
|
||||
endpoint: you-endpoint
|
||||
access-key: you-access-key
|
||||
secret-key: you-secret-key
|
||||
|
||||
# excel configuration
|
||||
excel:
|
||||
dir: './resource/excel/'
|
||||
dir: ./resource/excel/
|
||||
|
||||
# timer task db clear table
|
||||
Timer:
|
||||
start: true
|
||||
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
|
||||
detail: [
|
||||
# tableName: 需要清理的表名
|
||||
# compareField: 需要比较时间的字段
|
||||
# interval: 时间间隔, 具体配置详看 time.ParseDuration() 中字符串表示 且不能为负数
|
||||
# 2160h = 24 * 30 * 3 -> 三个月
|
||||
{ tableName: "sys_operation_records" , compareField: "created_at", interval: "2160h" },
|
||||
#{ tableName: "log2" , compareField: "created_at", interval: "2160h" }
|
||||
]
|
||||
# disk usage configuration
|
||||
disk-list:
|
||||
- mount-point: "/"
|
||||
|
||||
# 跨域配置
|
||||
# 需要配合 server/initialize/router.go#L32 使用
|
||||
# 需要配合 server/initialize/router.go -> `Router.Use(middleware.CorsByRules())` 使用
|
||||
cors:
|
||||
mode: whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝
|
||||
whitelist:
|
||||
- allow-origin: example1.com
|
||||
allow-headers: content-type
|
||||
allow-methods: GET, POST
|
||||
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
|
||||
allow-credentials: true # 布尔值
|
||||
- allow-origin: example2.com
|
||||
allow-headers: content-type
|
||||
allow-methods: GET, POST
|
||||
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
|
||||
allow-credentials: true # 布尔值
|
||||
mode: strict-whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝
|
||||
whitelist:
|
||||
- allow-origin: example1.com
|
||||
allow-headers: Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id
|
||||
allow-methods: POST, GET
|
||||
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
|
||||
|
||||
allow-credentials: true # 布尔值
|
||||
- allow-origin: example2.com
|
||||
allow-headers: content-type
|
||||
allow-methods: GET, POST
|
||||
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
|
||||
allow-credentials: true # 布尔值
|
||||
mcp:
|
||||
name: GVA_MCP
|
||||
version: v1.0.0
|
||||
sse_path: /sse
|
||||
message_path: /message
|
||||
url_prefix: ''
|
||||
+246
-194
@@ -1,196 +1,248 @@
|
||||
# github.com/flipped-aurora/gin-vue-admin/server Global Configuration
|
||||
|
||||
# jwt configuration
|
||||
jwt:
|
||||
signing-key: 'qmPlus'
|
||||
expires-time: 604800
|
||||
buffer-time: 86400
|
||||
issuer: 'qmPlus'
|
||||
# zap logger configuration
|
||||
zap:
|
||||
level: 'info'
|
||||
format: 'console'
|
||||
prefix: '[github.com/flipped-aurora/gin-vue-admin/server]'
|
||||
director: 'log'
|
||||
show-line: true
|
||||
encode-level: 'LowercaseColorLevelEncoder'
|
||||
stacktrace-key: 'stacktrace'
|
||||
log-in-console: true
|
||||
|
||||
# redis configuration
|
||||
redis:
|
||||
db: 0
|
||||
addr: '127.0.0.1:6379'
|
||||
password: ''
|
||||
|
||||
# email configuration
|
||||
email:
|
||||
to: 'xxx@qq.com'
|
||||
port: 465
|
||||
from: 'xxx@163.com'
|
||||
host: 'smtp.163.com'
|
||||
is-ssl: true
|
||||
secret: 'xxx'
|
||||
nickname: 'test'
|
||||
|
||||
# casbin configuration
|
||||
casbin:
|
||||
model-path: './resource/rbac_model.conf'
|
||||
|
||||
# system configuration
|
||||
system:
|
||||
env: 'public' # Change to "develop" to skip authentication for development mode
|
||||
addr: 8888
|
||||
db-type: 'mysql'
|
||||
oss-type: 'local' # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
|
||||
use-multipoint: false
|
||||
# IP限制次数 一个小时15000次
|
||||
iplimit-count: 15000
|
||||
# IP限制一个小时
|
||||
iplimit-time: 3600
|
||||
|
||||
# captcha configuration
|
||||
captcha:
|
||||
key-long: 6
|
||||
img-width: 240
|
||||
img-height: 80
|
||||
|
||||
# mysql connect configuration
|
||||
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://www.github.com/flipped-aurora/gin-vue-admin/server.com/docs/first)
|
||||
mysql:
|
||||
path: ''
|
||||
port: ''
|
||||
config: ''
|
||||
db-name: ''
|
||||
username: ''
|
||||
password: ''
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
log-mode: ""
|
||||
log-zap: false
|
||||
|
||||
# pgsql connect configuration
|
||||
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://www.github.com/flipped-aurora/gin-vue-admin/server.com/docs/first)
|
||||
pgsql:
|
||||
path: ''
|
||||
port: ''
|
||||
config: ''
|
||||
db-name: ''
|
||||
username: ''
|
||||
password: ''
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
log-mode: ""
|
||||
log-zap: false
|
||||
|
||||
db-list: [
|
||||
{
|
||||
disabled: true, # 是否启用
|
||||
type: "", # 数据库的类型,目前支持mysql、pgsql
|
||||
alias-name: "", # 数据库的名称,注意: alias-name 需要在db-list中唯一
|
||||
path: '',
|
||||
port: '',
|
||||
config: '',
|
||||
db-name: '',
|
||||
username: '',
|
||||
password: '',
|
||||
max-idle-conns: 10,
|
||||
max-open-conns: 100,
|
||||
log-mode: "",
|
||||
log-zap: false,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
# local configuration
|
||||
local:
|
||||
path: 'uploads/file'
|
||||
|
||||
# autocode configuration
|
||||
autocode:
|
||||
transfer-restart: true
|
||||
# root 自动适配项目根目录
|
||||
# 请不要手动配置,他会在项目加载的时候识别出根路径
|
||||
root: ""
|
||||
server: /server
|
||||
server-api: /api/v1/autocode
|
||||
server-initialize: /initialize
|
||||
server-model: /model/autocode
|
||||
server-request: /model/autocode/request/
|
||||
server-router: /router/autocode
|
||||
server-service: /service/autocode
|
||||
web: /web/src
|
||||
web-api: /api
|
||||
web-form: /view
|
||||
web-table: /view
|
||||
|
||||
# qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址)
|
||||
qiniu:
|
||||
zone: 'ZoneHuaDong'
|
||||
bucket: ''
|
||||
img-path: ''
|
||||
use-https: false
|
||||
access-key: ''
|
||||
secret-key: ''
|
||||
use-cdn-domains: false
|
||||
|
||||
# aliyun oss configuration
|
||||
aliyun-oss:
|
||||
endpoint: 'yourEndpoint'
|
||||
access-key-id: 'yourAccessKeyId'
|
||||
access-key-secret: 'yourAccessKeySecret'
|
||||
bucket-name: 'yourBucketName'
|
||||
bucket-url: 'yourBucketUrl'
|
||||
base-path: 'yourBasePath'
|
||||
|
||||
# tencent cos configuration
|
||||
tencent-cos:
|
||||
bucket: 'xxxxx-10005608'
|
||||
region: 'ap-shanghai'
|
||||
secret-id: 'xxxxxxxx'
|
||||
secret-key: 'xxxxxxxx'
|
||||
base-url: 'https://gin.vue.admin'
|
||||
path-prefix: 'github.com/flipped-aurora/gin-vue-admin/server'
|
||||
|
||||
# huawei obs configuration
|
||||
hua-wei-obs:
|
||||
path: 'you-path'
|
||||
bucket: 'you-bucket'
|
||||
endpoint: 'you-endpoint'
|
||||
access-key: 'you-access-key'
|
||||
secret-key: 'you-secret-key'
|
||||
|
||||
# excel configuration
|
||||
excel:
|
||||
dir: './resource/excel/'
|
||||
|
||||
# timer task db clear table
|
||||
Timer:
|
||||
start: true
|
||||
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
|
||||
detail: [
|
||||
# tableName: 需要清理的表名
|
||||
# compareField: 需要比较时间的字段
|
||||
# interval: 时间间隔, 具体配置详看 time.ParseDuration() 中字符串表示 且不能为负数
|
||||
# 2160h = 24 * 30 * 3 -> 三个月
|
||||
{ tableName: "sys_operation_records" , compareField: "created_at", interval: "2160h" },
|
||||
{ tableName: "jwt_blacklists" , compareField: "created_at", interval: "168h" }
|
||||
#{ tableName: "log2" , compareField: "created_at", interval: "2160h" }
|
||||
]
|
||||
|
||||
# 跨域配置
|
||||
# 需要配合 server/initialize/router.go#L32 使用
|
||||
endpoint: yourEndpoint
|
||||
access-key-id: yourAccessKeyId
|
||||
access-key-secret: yourAccessKeySecret
|
||||
bucket-name: yourBucketName
|
||||
bucket-url: yourBucketUrl
|
||||
base-path: yourBasePath
|
||||
autocode:
|
||||
web: web/src
|
||||
root: /Users/panghu/Documents/gva/gin-vue-admin
|
||||
server: server
|
||||
module: github.com/flipped-aurora/gin-vue-admin/server
|
||||
ai-path: ""
|
||||
aws-s3:
|
||||
bucket: xxxxx-10005608
|
||||
region: ap-shanghai
|
||||
endpoint: ""
|
||||
secret-id: your-secret-id
|
||||
secret-key: your-secret-key
|
||||
base-url: https://gin.vue.admin
|
||||
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
|
||||
s3-force-path-style: false
|
||||
disable-ssl: false
|
||||
captcha:
|
||||
key-long: 6
|
||||
img-width: 240
|
||||
img-height: 80
|
||||
open-captcha: 0
|
||||
open-captcha-timeout: 3600
|
||||
cloudflare-r2:
|
||||
bucket: xxxx0bucket
|
||||
base-url: https://gin.vue.admin.com
|
||||
path: uploads
|
||||
account-id: xxx_account_id
|
||||
access-key-id: xxx_key_id
|
||||
secret-access-key: xxx_secret_key
|
||||
cors:
|
||||
mode: whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝
|
||||
whitelist:
|
||||
- allow-origin: example1.com
|
||||
allow-headers: content-type
|
||||
allow-methods: GET, POST
|
||||
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
|
||||
allow-credentials: true # 布尔值
|
||||
- allow-origin: example2.com
|
||||
allow-headers: content-type
|
||||
allow-methods: GET, POST
|
||||
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
|
||||
allow-credentials: true # 布尔值
|
||||
mode: strict-whitelist
|
||||
whitelist:
|
||||
- allow-origin: example1.com
|
||||
allow-methods: POST, GET
|
||||
allow-headers: Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id
|
||||
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
|
||||
allow-credentials: true
|
||||
- allow-origin: example2.com
|
||||
allow-methods: GET, POST
|
||||
allow-headers: content-type
|
||||
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
|
||||
allow-credentials: true
|
||||
db-list:
|
||||
- type: ""
|
||||
alias-name: ""
|
||||
prefix: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
path: ""
|
||||
engine: ""
|
||||
log-mode: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
singular: false
|
||||
log-zap: false
|
||||
disable: true
|
||||
disk-list:
|
||||
- mount-point: /
|
||||
email:
|
||||
to: xxx@qq.com
|
||||
from: xxx@163.com
|
||||
host: smtp.163.com
|
||||
secret: xxx
|
||||
nickname: test
|
||||
port: 465
|
||||
is-ssl: true
|
||||
is-loginauth: false
|
||||
excel:
|
||||
dir: ./resource/excel/
|
||||
hua-wei-obs:
|
||||
path: you-path
|
||||
bucket: you-bucket
|
||||
endpoint: you-endpoint
|
||||
access-key: you-access-key
|
||||
secret-key: you-secret-key
|
||||
jwt:
|
||||
signing-key: 151a53c2-4dfa-42f2-aba3-0c3ad96535d9
|
||||
expires-time: 7d
|
||||
buffer-time: 1d
|
||||
issuer: qmPlus
|
||||
local:
|
||||
path: uploads/file
|
||||
store-path: uploads/file
|
||||
mcp:
|
||||
name: GVA_MCP
|
||||
version: v1.0.0
|
||||
sse_path: /sse
|
||||
message_path: /message
|
||||
url_prefix: ""
|
||||
minio:
|
||||
endpoint: yourEndpoint
|
||||
access-key-id: yourAccessKeyId
|
||||
access-key-secret: yourAccessKeySecret
|
||||
bucket-name: yourBucketName
|
||||
use-ssl: false
|
||||
base-path: ""
|
||||
bucket-url: http://host:9000/yourBucketName
|
||||
mongo:
|
||||
coll: ""
|
||||
options: ""
|
||||
database: ""
|
||||
username: ""
|
||||
password: ""
|
||||
auth-source: ""
|
||||
min-pool-size: 0
|
||||
max-pool-size: 100
|
||||
socket-timeout-ms: 0
|
||||
connect-timeout-ms: 0
|
||||
is-zap: false
|
||||
hosts:
|
||||
- host: ""
|
||||
port: ""
|
||||
mssql:
|
||||
prefix: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
path: ""
|
||||
engine: ""
|
||||
log-mode: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
singular: false
|
||||
log-zap: false
|
||||
mysql:
|
||||
prefix: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
path: ""
|
||||
engine: ""
|
||||
log-mode: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
singular: false
|
||||
log-zap: false
|
||||
oracle:
|
||||
prefix: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
path: ""
|
||||
engine: ""
|
||||
log-mode: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
singular: false
|
||||
log-zap: false
|
||||
pgsql:
|
||||
prefix: ""
|
||||
port: "5432"
|
||||
config: sslmode=disable TimeZone=Asia/Shanghai
|
||||
db-name: gva
|
||||
username: jutze
|
||||
password: jutze123
|
||||
path: 127.0.0.1
|
||||
engine: ""
|
||||
log-mode: error
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
singular: false
|
||||
log-zap: false
|
||||
qiniu:
|
||||
zone: ZoneHuaDong
|
||||
bucket: ""
|
||||
img-path: ""
|
||||
access-key: ""
|
||||
secret-key: ""
|
||||
use-https: false
|
||||
use-cdn-domains: false
|
||||
redis:
|
||||
name: ""
|
||||
addr: 127.0.0.1:6379
|
||||
password: ""
|
||||
db: 0
|
||||
useCluster: false
|
||||
clusterAddrs:
|
||||
- 172.21.0.3:7000
|
||||
- 172.21.0.4:7001
|
||||
- 172.21.0.2:7002
|
||||
redis-list:
|
||||
- name: cache
|
||||
addr: 127.0.0.1:6379
|
||||
password: ""
|
||||
db: 0
|
||||
useCluster: false
|
||||
clusterAddrs:
|
||||
- 172.21.0.3:7000
|
||||
- 172.21.0.4:7001
|
||||
- 172.21.0.2:7002
|
||||
sqlite:
|
||||
prefix: ""
|
||||
port: ""
|
||||
config: ""
|
||||
db-name: ""
|
||||
username: ""
|
||||
password: ""
|
||||
path: ""
|
||||
engine: ""
|
||||
log-mode: ""
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
singular: false
|
||||
log-zap: false
|
||||
system:
|
||||
db-type: pgsql
|
||||
oss-type: local
|
||||
router-prefix: ""
|
||||
addr: 8888
|
||||
iplimit-count: 15000
|
||||
iplimit-time: 3600
|
||||
use-multipoint: false
|
||||
use-redis: false
|
||||
use-mongo: false
|
||||
use-strict-auth: false
|
||||
tencent-cos:
|
||||
bucket: xxxxx-10005608
|
||||
region: ap-shanghai
|
||||
secret-id: your-secret-id
|
||||
secret-key: your-secret-key
|
||||
base-url: https://gin.vue.admin
|
||||
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
|
||||
zap:
|
||||
level: info
|
||||
prefix: '[github.com/flipped-aurora/gin-vue-admin/server]'
|
||||
format: console
|
||||
director: log
|
||||
encode-level: LowercaseColorLevelEncoder
|
||||
stacktrace-key: stacktrace
|
||||
show-line: true
|
||||
log-in-console: true
|
||||
retention-day: -1
|
||||
|
||||
+18
-13
@@ -1,17 +1,22 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Autocode struct {
|
||||
TransferRestart bool `mapstructure:"transfer-restart" json:"transferRestart" yaml:"transfer-restart"`
|
||||
Root string `mapstructure:"root" json:"root" yaml:"root"`
|
||||
Server string `mapstructure:"server" json:"server" yaml:"server"`
|
||||
SApi string `mapstructure:"server-api" json:"serverApi" yaml:"server-api"`
|
||||
SInitialize string `mapstructure:"server-initialize" json:"serverInitialize" yaml:"server-initialize"`
|
||||
SModel string `mapstructure:"server-model" json:"serverModel" yaml:"server-model"`
|
||||
SRequest string `mapstructure:"server-request" json:"serverRequest" yaml:"server-request"`
|
||||
SRouter string `mapstructure:"server-router" json:"serverRouter" yaml:"server-router"`
|
||||
SService string `mapstructure:"server-service" json:"serverService" yaml:"server-service"`
|
||||
Web string `mapstructure:"web" json:"web" yaml:"web"`
|
||||
WApi string `mapstructure:"web-api" json:"webApi" yaml:"web-api"`
|
||||
WForm string `mapstructure:"web-form" json:"webForm" yaml:"web-form"`
|
||||
WTable string `mapstructure:"web-table" json:"webTable" yaml:"web-table"`
|
||||
Web string `mapstructure:"web" json:"web" yaml:"web"`
|
||||
Root string `mapstructure:"root" json:"root" yaml:"root"`
|
||||
Server string `mapstructure:"server" json:"server" yaml:"server"`
|
||||
Module string `mapstructure:"module" json:"module" yaml:"module"`
|
||||
AiPath string `mapstructure:"ai-path" json:"ai-path" yaml:"ai-path"`
|
||||
}
|
||||
|
||||
func (a *Autocode) WebRoot() string {
|
||||
webs := strings.Split(a.Web, "/")
|
||||
if len(webs) == 0 {
|
||||
webs = strings.Split(a.Web, "\\")
|
||||
}
|
||||
return filepath.Join(webs...)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package config
|
||||
|
||||
type Captcha struct {
|
||||
KeyLong int `mapstructure:"key-long" json:"keyLong" yaml:"key-long"` // 验证码长度
|
||||
ImgWidth int `mapstructure:"img-width" json:"imgWidth" yaml:"img-width"` // 验证码宽度
|
||||
ImgHeight int `mapstructure:"img-height" json:"imgHeight" yaml:"img-height"` // 验证码高度
|
||||
KeyLong int `mapstructure:"key-long" json:"key-long" yaml:"key-long"` // 验证码长度
|
||||
ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"` // 验证码宽度
|
||||
ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"` // 验证码高度
|
||||
OpenCaptcha int `mapstructure:"open-captcha" json:"open-captcha" yaml:"open-captcha"` // 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码次数,如3代表错误三次后出现验证码
|
||||
OpenCaptchaTimeOut int `mapstructure:"open-captcha-timeout" json:"open-captcha-timeout" yaml:"open-captcha-timeout"` // 防爆破验证码超时时间,单位:s(秒)
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package config
|
||||
|
||||
type Casbin struct {
|
||||
ModelPath string `mapstructure:"model-path" json:"modelPath" yaml:"model-path"` // 存放casbin模型的相对路径
|
||||
}
|
||||
+28
-17
@@ -1,29 +1,40 @@
|
||||
package config
|
||||
|
||||
type Server struct {
|
||||
JWT JWT `mapstructure:"jwt" json:"jwt" yaml:"jwt"`
|
||||
Zap Zap `mapstructure:"zap" json:"zap" yaml:"zap"`
|
||||
Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"`
|
||||
Email Email `mapstructure:"email" json:"email" yaml:"email"`
|
||||
Casbin Casbin `mapstructure:"casbin" json:"casbin" yaml:"casbin"`
|
||||
System System `mapstructure:"system" json:"system" yaml:"system"`
|
||||
Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"`
|
||||
JWT JWT `mapstructure:"jwt" json:"jwt" yaml:"jwt"`
|
||||
Zap Zap `mapstructure:"zap" json:"zap" yaml:"zap"`
|
||||
Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"`
|
||||
RedisList []Redis `mapstructure:"redis-list" json:"redis-list" yaml:"redis-list"`
|
||||
Mongo Mongo `mapstructure:"mongo" json:"mongo" yaml:"mongo"`
|
||||
Email Email `mapstructure:"email" json:"email" yaml:"email"`
|
||||
System System `mapstructure:"system" json:"system" yaml:"system"`
|
||||
Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"`
|
||||
// auto
|
||||
AutoCode Autocode `mapstructure:"autoCode" json:"autoCode" yaml:"autoCode"`
|
||||
AutoCode Autocode `mapstructure:"autocode" json:"autocode" yaml:"autocode"`
|
||||
// gorm
|
||||
Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
|
||||
Pgsql Pgsql `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"`
|
||||
DBList []DB `mapstructure:"db-list" json:"db-list" yaml:"db-list"`
|
||||
Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
|
||||
Mssql Mssql `mapstructure:"mssql" json:"mssql" yaml:"mssql"`
|
||||
Pgsql Pgsql `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"`
|
||||
Oracle Oracle `mapstructure:"oracle" json:"oracle" yaml:"oracle"`
|
||||
Sqlite Sqlite `mapstructure:"sqlite" json:"sqlite" yaml:"sqlite"`
|
||||
DBList []SpecializedDB `mapstructure:"db-list" json:"db-list" yaml:"db-list"`
|
||||
// oss
|
||||
Local Local `mapstructure:"local" json:"local" yaml:"local"`
|
||||
Qiniu Qiniu `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"`
|
||||
AliyunOSS AliyunOSS `mapstructure:"aliyun-oss" json:"aliyunOSS" yaml:"aliyun-oss"`
|
||||
HuaWeiObs HuaWeiObs `mapstructure:"hua-wei-obs" json:"huaWeiObs" yaml:"hua-wei-obs"`
|
||||
TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencentCOS" yaml:"tencent-cos"`
|
||||
Local Local `mapstructure:"local" json:"local" yaml:"local"`
|
||||
Qiniu Qiniu `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"`
|
||||
AliyunOSS AliyunOSS `mapstructure:"aliyun-oss" json:"aliyun-oss" yaml:"aliyun-oss"`
|
||||
HuaWeiObs HuaWeiObs `mapstructure:"hua-wei-obs" json:"hua-wei-obs" yaml:"hua-wei-obs"`
|
||||
TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencent-cos" yaml:"tencent-cos"`
|
||||
AwsS3 AwsS3 `mapstructure:"aws-s3" json:"aws-s3" yaml:"aws-s3"`
|
||||
CloudflareR2 CloudflareR2 `mapstructure:"cloudflare-r2" json:"cloudflare-r2" yaml:"cloudflare-r2"`
|
||||
Minio Minio `mapstructure:"minio" json:"minio" yaml:"minio"`
|
||||
|
||||
Excel Excel `mapstructure:"excel" json:"excel" yaml:"excel"`
|
||||
Timer Timer `mapstructure:"timer" json:"timer" yaml:"timer"`
|
||||
|
||||
DiskList []DiskList `mapstructure:"disk-list" json:"disk-list" yaml:"disk-list"`
|
||||
|
||||
// 跨域配置
|
||||
Cors CORS `mapstructure:"cors" json:"cors" yaml:"cors"`
|
||||
|
||||
// MCP配置
|
||||
MCP MCP `mapstructure:"mcp" json:"mcp" yaml:"mcp"`
|
||||
}
|
||||
|
||||
+48
-16
@@ -1,21 +1,53 @@
|
||||
package config
|
||||
|
||||
type DB struct {
|
||||
Disable bool `mapstructure:"disable" json:"disable" yaml:"disable"`
|
||||
Type string `mapstructure:"type" json:"type" yaml:"type"`
|
||||
AliasName string `mapstructure:"alias-name" json:"alias-name" yaml:"alias-name"`
|
||||
Path string `mapstructure:"path" json:"path" yaml:"path"` // 服务器地址:端口
|
||||
Port string `mapstructure:"port" json:"port" yaml:"port"` //:端口
|
||||
Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置
|
||||
Dbname string `mapstructure:"db-name" json:"dbname" yaml:"db-name"` // 数据库名
|
||||
Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库用户名
|
||||
Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码
|
||||
MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` // 空闲中的最大连接数
|
||||
MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
|
||||
LogMode string `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"` // 是否开启Gorm全局日志
|
||||
LogZap bool `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"`
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
type DsnProvider interface {
|
||||
Dsn() string
|
||||
}
|
||||
|
||||
func (m *DB) Dsn() string {
|
||||
return m.Username + ":" + m.Password + "@tcp(" + m.Path + ":" + m.Port + ")/" + m.Dbname + "?" + m.Config
|
||||
// Embeded 结构体可以压平到上一层,从而保持 config 文件的结构和原来一样
|
||||
// 见 playground: https://go.dev/play/p/KIcuhqEoxmY
|
||||
|
||||
// GeneralDB 也被 Pgsql 和 Mysql 原样使用
|
||||
type GeneralDB struct {
|
||||
Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` // 数据库前缀
|
||||
Port string `mapstructure:"port" json:"port" yaml:"port"` // 数据库端口
|
||||
Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置
|
||||
Dbname string `mapstructure:"db-name" json:"db-name" yaml:"db-name"` // 数据库名
|
||||
Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库账号
|
||||
Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码
|
||||
Path string `mapstructure:"path" json:"path" yaml:"path"` // 数据库地址
|
||||
Engine string `mapstructure:"engine" json:"engine" yaml:"engine" default:"InnoDB"` // 数据库引擎,默认InnoDB
|
||||
LogMode string `mapstructure:"log-mode" json:"log-mode" yaml:"log-mode"` // 是否开启Gorm全局日志
|
||||
MaxIdleConns int `mapstructure:"max-idle-conns" json:"max-idle-conns" yaml:"max-idle-conns"` // 空闲中的最大连接数
|
||||
MaxOpenConns int `mapstructure:"max-open-conns" json:"max-open-conns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
|
||||
Singular bool `mapstructure:"singular" json:"singular" yaml:"singular"` // 是否开启全局禁用复数,true表示开启
|
||||
LogZap bool `mapstructure:"log-zap" json:"log-zap" yaml:"log-zap"` // 是否通过zap写入日志文件
|
||||
}
|
||||
|
||||
func (c GeneralDB) LogLevel() logger.LogLevel {
|
||||
switch strings.ToLower(c.LogMode) {
|
||||
case "silent":
|
||||
return logger.Silent
|
||||
case "error":
|
||||
return logger.Error
|
||||
case "warn":
|
||||
return logger.Warn
|
||||
case "info":
|
||||
return logger.Info
|
||||
default:
|
||||
return logger.Info
|
||||
}
|
||||
}
|
||||
|
||||
type SpecializedDB struct {
|
||||
Type string `mapstructure:"type" json:"type" yaml:"type"`
|
||||
AliasName string `mapstructure:"alias-name" json:"alias-name" yaml:"alias-name"`
|
||||
GeneralDB `yaml:",inline" mapstructure:",squash"`
|
||||
Disable bool `mapstructure:"disable" json:"disable" yaml:"disable"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
type Disk struct {
|
||||
MountPoint string `mapstructure:"mount-point" json:"mount-point" yaml:"mount-point"`
|
||||
}
|
||||
|
||||
type DiskList struct {
|
||||
Disk `yaml:",inline" mapstructure:",squash"`
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package config
|
||||
|
||||
type Email struct {
|
||||
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔
|
||||
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口
|
||||
From string `mapstructure:"from" json:"from" yaml:"from"` // 收件人
|
||||
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址
|
||||
IsSSL bool `mapstructure:"is-ssl" json:"isSSL" yaml:"is-ssl"` // 是否SSL
|
||||
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥
|
||||
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称
|
||||
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用
|
||||
From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱
|
||||
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
|
||||
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
|
||||
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱
|
||||
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
|
||||
IsSSL bool `mapstructure:"is-ssl" json:"is-ssl" yaml:"is-ssl"` // 是否SSL 是否开启SSL
|
||||
IsLoginAuth bool `mapstructure:"is-loginauth" json:"is-loginauth" yaml:"is-loginauth"` // 是否LoginAuth 是否使用LoginAuth认证方式(适用于IBM、微软邮箱服务器等)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package config
|
||||
|
||||
type Mssql struct {
|
||||
GeneralDB `yaml:",inline" mapstructure:",squash"`
|
||||
}
|
||||
|
||||
// Dsn "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
|
||||
func (m *Mssql) Dsn() string {
|
||||
return "sqlserver://" + m.Username + ":" + m.Password + "@" + m.Path + ":" + m.Port + "?database=" + m.Dbname + "&encrypt=disable"
|
||||
}
|
||||
@@ -1,16 +1,7 @@
|
||||
package config
|
||||
|
||||
type Mysql struct {
|
||||
Path string `mapstructure:"path" json:"path" yaml:"path"` // 服务器地址
|
||||
Port string `mapstructure:"port" json:"port" yaml:"port"` // 端口
|
||||
Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置
|
||||
Dbname string `mapstructure:"db-name" json:"dbname" yaml:"db-name"` // 数据库名
|
||||
Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库用户名
|
||||
Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码
|
||||
MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` // 空闲中的最大连接数
|
||||
MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
|
||||
LogMode string `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"` // 是否开启Gorm全局日志
|
||||
LogZap bool `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"` // 是否通过zap写入日志文件
|
||||
GeneralDB `yaml:",inline" mapstructure:",squash"`
|
||||
}
|
||||
|
||||
func (m *Mysql) Dsn() string {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type Oracle struct {
|
||||
GeneralDB `yaml:",inline" mapstructure:",squash"`
|
||||
}
|
||||
|
||||
func (m *Oracle) Dsn() string {
|
||||
dsn := fmt.Sprintf("oracle://%s:%s@%s/%s?%s", url.PathEscape(m.Username), url.PathEscape(m.Password),
|
||||
net.JoinHostPort(m.Path, m.Port), url.PathEscape(m.Dbname), m.Config)
|
||||
return dsn
|
||||
|
||||
}
|
||||
@@ -1,16 +1,7 @@
|
||||
package config
|
||||
|
||||
type Pgsql struct {
|
||||
Path string `mapstructure:"path" json:"path" yaml:"path"` // 服务器地址:端口
|
||||
Port string `mapstructure:"port" json:"port" yaml:"port"` //:端口
|
||||
Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置
|
||||
Dbname string `mapstructure:"db-name" json:"dbname" yaml:"db-name"` // 数据库名
|
||||
Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库用户名
|
||||
Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码
|
||||
MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` // 空闲中的最大连接数
|
||||
MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
|
||||
LogMode string `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"` // 是否开启Gorm全局日志
|
||||
LogZap bool `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"` // 是否通过zap写入日志文件
|
||||
GeneralDB `yaml:",inline" mapstructure:",squash"`
|
||||
}
|
||||
|
||||
// Dsn 基于配置文件获取 dsn
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Sqlite struct {
|
||||
GeneralDB `yaml:",inline" mapstructure:",squash"`
|
||||
}
|
||||
|
||||
func (s *Sqlite) Dsn() string {
|
||||
return filepath.Join(s.Path, s.Dbname+".db")
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package config
|
||||
|
||||
type JWT struct {
|
||||
SigningKey string `mapstructure:"signing-key" json:"signingKey" yaml:"signing-key"` // jwt签名
|
||||
ExpiresTime int64 `mapstructure:"expires-time" json:"expiresTime" yaml:"expires-time"` // 过期时间
|
||||
BufferTime int64 `mapstructure:"buffer-time" json:"bufferTime" yaml:"buffer-time"` // 缓冲时间
|
||||
Issuer string `mapstructure:"issuer" json:"issuer" yaml:"issuer"` // 签发者
|
||||
SigningKey string `mapstructure:"signing-key" json:"signing-key" yaml:"signing-key"` // jwt签名
|
||||
ExpiresTime string `mapstructure:"expires-time" json:"expires-time" yaml:"expires-time"` // 过期时间
|
||||
BufferTime string `mapstructure:"buffer-time" json:"buffer-time" yaml:"buffer-time"` // 缓冲时间
|
||||
Issuer string `mapstructure:"issuer" json:"issuer" yaml:"issuer"` // 签发者
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
type MCP struct {
|
||||
Name string `mapstructure:"name" json:"name" yaml:"name"` // MCP名称
|
||||
Version string `mapstructure:"version" json:"version" yaml:"version"` // MCP版本
|
||||
SSEPath string `mapstructure:"sse_path" json:"sse_path" yaml:"sse_path"` // SSE路径
|
||||
MessagePath string `mapstructure:"message_path" json:"message_path" yaml:"message_path"` // 消息路径
|
||||
UrlPrefix string `mapstructure:"url_prefix" json:"url_prefix" yaml:"url_prefix"` // URL前缀
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Mongo struct {
|
||||
Coll string `json:"coll" yaml:"coll" mapstructure:"coll"` // collection name
|
||||
Options string `json:"options" yaml:"options" mapstructure:"options"` // mongodb options
|
||||
Database string `json:"database" yaml:"database" mapstructure:"database"` // database name
|
||||
Username string `json:"username" yaml:"username" mapstructure:"username"` // 用户名
|
||||
Password string `json:"password" yaml:"password" mapstructure:"password"` // 密码
|
||||
AuthSource string `json:"auth-source" yaml:"auth-source" mapstructure:"auth-source"` // 验证数据库
|
||||
MinPoolSize uint64 `json:"min-pool-size" yaml:"min-pool-size" mapstructure:"min-pool-size"` // 最小连接池
|
||||
MaxPoolSize uint64 `json:"max-pool-size" yaml:"max-pool-size" mapstructure:"max-pool-size"` // 最大连接池
|
||||
SocketTimeoutMs int64 `json:"socket-timeout-ms" yaml:"socket-timeout-ms" mapstructure:"socket-timeout-ms"` // socket超时时间
|
||||
ConnectTimeoutMs int64 `json:"connect-timeout-ms" yaml:"connect-timeout-ms" mapstructure:"connect-timeout-ms"` // 连接超时时间
|
||||
IsZap bool `json:"is-zap" yaml:"is-zap" mapstructure:"is-zap"` // 是否开启zap日志
|
||||
Hosts []*MongoHost `json:"hosts" yaml:"hosts" mapstructure:"hosts"` // 主机列表
|
||||
}
|
||||
|
||||
type MongoHost struct {
|
||||
Host string `json:"host" yaml:"host" mapstructure:"host"` // ip地址
|
||||
Port string `json:"port" yaml:"port" mapstructure:"port"` // 端口
|
||||
}
|
||||
|
||||
// Uri .
|
||||
func (x *Mongo) Uri() string {
|
||||
length := len(x.Hosts)
|
||||
hosts := make([]string, 0, length)
|
||||
for i := 0; i < length; i++ {
|
||||
if x.Hosts[i].Host != "" && x.Hosts[i].Port != "" {
|
||||
hosts = append(hosts, x.Hosts[i].Host+":"+x.Hosts[i].Port)
|
||||
}
|
||||
}
|
||||
if x.Options != "" {
|
||||
return fmt.Sprintf("mongodb://%s/%s?%s", strings.Join(hosts, ","), x.Database, x.Options)
|
||||
}
|
||||
return fmt.Sprintf("mongodb://%s/%s", strings.Join(hosts, ","), x.Database)
|
||||
}
|
||||
@@ -2,9 +2,9 @@ package config
|
||||
|
||||
type AliyunOSS struct {
|
||||
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
|
||||
AccessKeyId string `mapstructure:"access-key-id" json:"accessKeyId" yaml:"access-key-id"`
|
||||
AccessKeySecret string `mapstructure:"access-key-secret" json:"accessKeySecret" yaml:"access-key-secret"`
|
||||
BucketName string `mapstructure:"bucket-name" json:"bucketName" yaml:"bucket-name"`
|
||||
BucketUrl string `mapstructure:"bucket-url" json:"bucketUrl" yaml:"bucket-url"`
|
||||
BasePath string `mapstructure:"base-path" json:"basePath" yaml:"base-path"`
|
||||
AccessKeyId string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
|
||||
AccessKeySecret string `mapstructure:"access-key-secret" json:"access-key-secret" yaml:"access-key-secret"`
|
||||
BucketName string `mapstructure:"bucket-name" json:"bucket-name" yaml:"bucket-name"`
|
||||
BucketUrl string `mapstructure:"bucket-url" json:"bucket-url" yaml:"bucket-url"`
|
||||
BasePath string `mapstructure:"base-path" json:"base-path" yaml:"base-path"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package config
|
||||
|
||||
type AwsS3 struct {
|
||||
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
|
||||
Region string `mapstructure:"region" json:"region" yaml:"region"`
|
||||
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
|
||||
SecretID string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
|
||||
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
|
||||
BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
|
||||
PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"`
|
||||
S3ForcePathStyle bool `mapstructure:"s3-force-path-style" json:"s3-force-path-style" yaml:"s3-force-path-style"`
|
||||
DisableSSL bool `mapstructure:"disable-ssl" json:"disable-ssl" yaml:"disable-ssl"`
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package config
|
||||
|
||||
type CloudflareR2 struct {
|
||||
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
|
||||
BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
|
||||
Path string `mapstructure:"path" json:"path" yaml:"path"`
|
||||
AccountID string `mapstructure:"account-id" json:"account-id" yaml:"account-id"`
|
||||
AccessKeyID string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
|
||||
SecretAccessKey string `mapstructure:"secret-access-key" json:"secret-access-key" yaml:"secret-access-key"`
|
||||
}
|
||||
@@ -4,6 +4,6 @@ type HuaWeiObs struct {
|
||||
Path string `mapstructure:"path" json:"path" yaml:"path"`
|
||||
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
|
||||
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
|
||||
AccessKey string `mapstructure:"access-key" json:"accessKey" yaml:"access-key"`
|
||||
SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"`
|
||||
AccessKey string `mapstructure:"access-key" json:"access-key" yaml:"access-key"`
|
||||
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package config
|
||||
|
||||
type Local struct {
|
||||
Path string `mapstructure:"path" json:"path" yaml:"path"` // 本地文件路径
|
||||
Path string `mapstructure:"path" json:"path" yaml:"path"` // 本地文件访问路径
|
||||
StorePath string `mapstructure:"store-path" json:"store-path" yaml:"store-path"` // 本地文件存储路径
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package config
|
||||
|
||||
type Minio struct {
|
||||
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
|
||||
AccessKeyId string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
|
||||
AccessKeySecret string `mapstructure:"access-key-secret" json:"access-key-secret" yaml:"access-key-secret"`
|
||||
BucketName string `mapstructure:"bucket-name" json:"bucket-name" yaml:"bucket-name"`
|
||||
UseSSL bool `mapstructure:"use-ssl" json:"use-ssl" yaml:"use-ssl"`
|
||||
BasePath string `mapstructure:"base-path" json:"base-path" yaml:"base-path"`
|
||||
BucketUrl string `mapstructure:"bucket-url" json:"bucket-url" yaml:"bucket-url"`
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package config
|
||||
|
||||
type Qiniu struct {
|
||||
Zone string `mapstructure:"zone" json:"zone" yaml:"zone"` // 存储区域
|
||||
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` // 空间名称
|
||||
ImgPath string `mapstructure:"img-path" json:"imgPath" yaml:"img-path"` // CDN加速域名
|
||||
UseHTTPS bool `mapstructure:"use-https" json:"useHttps" yaml:"use-https"` // 是否使用https
|
||||
AccessKey string `mapstructure:"access-key" json:"accessKey" yaml:"access-key"` // 秘钥AK
|
||||
SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"` // 秘钥SK
|
||||
UseCdnDomains bool `mapstructure:"use-cdn-domains" json:"useCdnDomains" yaml:"use-cdn-domains"` // 上传是否使用CDN上传加速
|
||||
Zone string `mapstructure:"zone" json:"zone" yaml:"zone"` // 存储区域
|
||||
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` // 空间名称
|
||||
ImgPath string `mapstructure:"img-path" json:"img-path" yaml:"img-path"` // CDN加速域名
|
||||
AccessKey string `mapstructure:"access-key" json:"access-key" yaml:"access-key"` // 秘钥AK
|
||||
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"` // 秘钥SK
|
||||
UseHTTPS bool `mapstructure:"use-https" json:"use-https" yaml:"use-https"` // 是否使用https
|
||||
UseCdnDomains bool `mapstructure:"use-cdn-domains" json:"use-cdn-domains" yaml:"use-cdn-domains"` // 上传是否使用CDN上传加速
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package config
|
||||
type TencentCOS struct {
|
||||
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
|
||||
Region string `mapstructure:"region" json:"region" yaml:"region"`
|
||||
SecretID string `mapstructure:"secret-id" json:"secretID" yaml:"secret-id"`
|
||||
SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"`
|
||||
BaseURL string `mapstructure:"base-url" json:"baseURL" yaml:"base-url"`
|
||||
PathPrefix string `mapstructure:"path-prefix" json:"pathPrefix" yaml:"path-prefix"`
|
||||
SecretID string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
|
||||
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
|
||||
BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
|
||||
PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"`
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package config
|
||||
|
||||
type Redis struct {
|
||||
DB int `mapstructure:"db" json:"db" yaml:"db"` // redis的哪个数据库
|
||||
Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` // 服务器地址:端口
|
||||
Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码
|
||||
Name string `mapstructure:"name" json:"name" yaml:"name"` // 代表当前实例的名字
|
||||
Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` // 服务器地址:端口
|
||||
Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码
|
||||
DB int `mapstructure:"db" json:"db" yaml:"db"` // 单实例模式下redis的哪个数据库
|
||||
UseCluster bool `mapstructure:"useCluster" json:"useCluster" yaml:"useCluster"` // 是否使用集群模式
|
||||
ClusterAddrs []string `mapstructure:"clusterAddrs" json:"clusterAddrs" yaml:"clusterAddrs"` // 集群模式下的节点地址列表
|
||||
}
|
||||
|
||||
+10
-7
@@ -1,11 +1,14 @@
|
||||
package config
|
||||
|
||||
type System struct {
|
||||
Env string `mapstructure:"env" json:"env" yaml:"env"` // 环境值
|
||||
Addr int `mapstructure:"addr" json:"addr" yaml:"addr"` // 端口值
|
||||
DbType string `mapstructure:"db-type" json:"dbType" yaml:"db-type"` // 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql
|
||||
OssType string `mapstructure:"oss-type" json:"ossType" yaml:"oss-type"` // Oss类型
|
||||
UseMultipoint bool `mapstructure:"use-multipoint" json:"useMultipoint" yaml:"use-multipoint"` // 多点登录拦截
|
||||
LimitCountIP int `mapstructure:"iplimit-count" json:"iplimitCount" yaml:"iplimit-count"`
|
||||
LimitTimeIP int `mapstructure:"iplimit-time" json:"iplimitTime" yaml:"iplimit-time"`
|
||||
DbType string `mapstructure:"db-type" json:"db-type" yaml:"db-type"` // 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql
|
||||
OssType string `mapstructure:"oss-type" json:"oss-type" yaml:"oss-type"` // Oss类型
|
||||
RouterPrefix string `mapstructure:"router-prefix" json:"router-prefix" yaml:"router-prefix"`
|
||||
Addr int `mapstructure:"addr" json:"addr" yaml:"addr"` // 端口值
|
||||
LimitCountIP int `mapstructure:"iplimit-count" json:"iplimit-count" yaml:"iplimit-count"`
|
||||
LimitTimeIP int `mapstructure:"iplimit-time" json:"iplimit-time" yaml:"iplimit-time"`
|
||||
UseMultipoint bool `mapstructure:"use-multipoint" json:"use-multipoint" yaml:"use-multipoint"` // 多点登录拦截
|
||||
UseRedis bool `mapstructure:"use-redis" json:"use-redis" yaml:"use-redis"` // 使用redis
|
||||
UseMongo bool `mapstructure:"use-mongo" json:"use-mongo" yaml:"use-mongo"` // 使用mongo
|
||||
UseStrictAuth bool `mapstructure:"use-strict-auth" json:"use-strict-auth" yaml:"use-strict-auth"` // 使用树形角色分配模式
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package config
|
||||
|
||||
type Timer struct {
|
||||
Start bool `mapstructure:"start" json:"start" yaml:"start"` // 是否启用
|
||||
Spec string `mapstructure:"spec" json:"spec" yaml:"spec"` // CRON表达式
|
||||
Detail []Detail `mapstructure:"detail" json:"detail" yaml:"detail"`
|
||||
}
|
||||
|
||||
type Detail struct {
|
||||
TableName string `mapstructure:"tableName" json:"tableName" yaml:"tableName"` // 需要清理的表名
|
||||
CompareField string `mapstructure:"compareField" json:"compareField" yaml:"compareField"` // 需要比较时间的字段
|
||||
Interval string `mapstructure:"interval" json:"interval" yaml:"interval"` // 时间间隔
|
||||
}
|
||||
+67
-8
@@ -1,12 +1,71 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"go.uber.org/zap/zapcore"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Zap struct {
|
||||
Level string `mapstructure:"level" json:"level" yaml:"level"` // 级别
|
||||
Format string `mapstructure:"format" json:"format" yaml:"format"` // 输出
|
||||
Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` // 日志前缀
|
||||
Director string `mapstructure:"director" json:"director" yaml:"director"` // 日志文件夹
|
||||
ShowLine bool `mapstructure:"show-line" json:"showLine" yaml:"showLine"` // 显示行
|
||||
EncodeLevel string `mapstructure:"encode-level" json:"encodeLevel" yaml:"encode-level"` // 编码级
|
||||
StacktraceKey string `mapstructure:"stacktrace-key" json:"stacktraceKey" yaml:"stacktrace-key"` // 栈名
|
||||
LogInConsole bool `mapstructure:"log-in-console" json:"logInConsole" yaml:"log-in-console"` // 输出控制台
|
||||
Level string `mapstructure:"level" json:"level" yaml:"level"` // 级别
|
||||
Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` // 日志前缀
|
||||
Format string `mapstructure:"format" json:"format" yaml:"format"` // 输出
|
||||
Director string `mapstructure:"director" json:"director" yaml:"director"` // 日志文件夹
|
||||
EncodeLevel string `mapstructure:"encode-level" json:"encode-level" yaml:"encode-level"` // 编码级
|
||||
StacktraceKey string `mapstructure:"stacktrace-key" json:"stacktrace-key" yaml:"stacktrace-key"` // 栈名
|
||||
ShowLine bool `mapstructure:"show-line" json:"show-line" yaml:"show-line"` // 显示行
|
||||
LogInConsole bool `mapstructure:"log-in-console" json:"log-in-console" yaml:"log-in-console"` // 输出控制台
|
||||
RetentionDay int `mapstructure:"retention-day" json:"retention-day" yaml:"retention-day"` // 日志保留天数
|
||||
}
|
||||
|
||||
// Levels 根据字符串转化为 zapcore.Levels
|
||||
func (c *Zap) Levels() []zapcore.Level {
|
||||
levels := make([]zapcore.Level, 0, 7)
|
||||
level, err := zapcore.ParseLevel(c.Level)
|
||||
if err != nil {
|
||||
level = zapcore.DebugLevel
|
||||
}
|
||||
for ; level <= zapcore.FatalLevel; level++ {
|
||||
levels = append(levels, level)
|
||||
}
|
||||
return levels
|
||||
}
|
||||
|
||||
func (c *Zap) Encoder() zapcore.Encoder {
|
||||
config := zapcore.EncoderConfig{
|
||||
TimeKey: "time",
|
||||
NameKey: "name",
|
||||
LevelKey: "level",
|
||||
CallerKey: "caller",
|
||||
MessageKey: "message",
|
||||
StacktraceKey: c.StacktraceKey,
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeTime: func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
|
||||
encoder.AppendString(c.Prefix + t.Format("2006-01-02 15:04:05.000"))
|
||||
},
|
||||
EncodeLevel: c.LevelEncoder(),
|
||||
EncodeCaller: zapcore.FullCallerEncoder,
|
||||
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||
}
|
||||
if c.Format == "json" {
|
||||
return zapcore.NewJSONEncoder(config)
|
||||
}
|
||||
return zapcore.NewConsoleEncoder(config)
|
||||
|
||||
}
|
||||
|
||||
// LevelEncoder 根据 EncodeLevel 返回 zapcore.LevelEncoder
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (c *Zap) LevelEncoder() zapcore.LevelEncoder {
|
||||
switch {
|
||||
case c.EncodeLevel == "LowercaseLevelEncoder": // 小写编码器(默认)
|
||||
return zapcore.LowercaseLevelEncoder
|
||||
case c.EncodeLevel == "LowercaseColorLevelEncoder": // 小写编码器带颜色
|
||||
return zapcore.LowercaseColorLevelEncoder
|
||||
case c.EncodeLevel == "CapitalLevelEncoder": // 大写编码器
|
||||
return zapcore.CapitalLevelEncoder
|
||||
case c.EncodeLevel == "CapitalColorLevelEncoder": // 大写编码器带颜色
|
||||
return zapcore.CapitalColorLevelEncoder
|
||||
default:
|
||||
return zapcore.LowercaseLevelEncoder
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package internal
|
||||
|
||||
const (
|
||||
ConfigEnv = "GVA_CONFIG"
|
||||
ConfigDefaultFile = "config.yaml"
|
||||
ConfigTestFile = "config.test.yaml"
|
||||
ConfigDebugFile = "config.debug.yaml"
|
||||
ConfigReleaseFile = "config.release.yaml"
|
||||
)
|
||||
@@ -0,0 +1,121 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Cutter 实现 io.Writer 接口
|
||||
// 用于日志切割, strings.Join([]string{director,layout, formats..., level+".log"}, os.PathSeparator)
|
||||
type Cutter struct {
|
||||
level string // 日志级别(debug, info, warn, error, dpanic, panic, fatal)
|
||||
layout string // 时间格式 2006-01-02 15:04:05
|
||||
formats []string // 自定义参数([]string{Director,"2006-01-02", "business"(此参数可不写), level+".log"}
|
||||
director string // 日志文件夹
|
||||
retentionDay int //日志保留天数
|
||||
file *os.File // 文件句柄
|
||||
mutex *sync.RWMutex // 读写锁
|
||||
}
|
||||
|
||||
type CutterOption func(*Cutter)
|
||||
|
||||
// CutterWithLayout 时间格式
|
||||
func CutterWithLayout(layout string) CutterOption {
|
||||
return func(c *Cutter) {
|
||||
c.layout = layout
|
||||
}
|
||||
}
|
||||
|
||||
// CutterWithFormats 格式化参数
|
||||
func CutterWithFormats(format ...string) CutterOption {
|
||||
return func(c *Cutter) {
|
||||
if len(format) > 0 {
|
||||
c.formats = format
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewCutter(director string, level string, retentionDay int, options ...CutterOption) *Cutter {
|
||||
rotate := &Cutter{
|
||||
level: level,
|
||||
director: director,
|
||||
retentionDay: retentionDay,
|
||||
mutex: new(sync.RWMutex),
|
||||
}
|
||||
for i := 0; i < len(options); i++ {
|
||||
options[i](rotate)
|
||||
}
|
||||
return rotate
|
||||
}
|
||||
|
||||
// Write satisfies the io.Writer interface. It writes to the
|
||||
// appropriate file handle that is currently being used.
|
||||
// If we have reached rotation time, the target file gets
|
||||
// automatically rotated, and also purged if necessary.
|
||||
func (c *Cutter) Write(bytes []byte) (n int, err error) {
|
||||
c.mutex.Lock()
|
||||
defer func() {
|
||||
if c.file != nil {
|
||||
_ = c.file.Close()
|
||||
c.file = nil
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
}()
|
||||
length := len(c.formats)
|
||||
values := make([]string, 0, 3+length)
|
||||
values = append(values, c.director)
|
||||
if c.layout != "" {
|
||||
values = append(values, time.Now().Format(c.layout))
|
||||
}
|
||||
for i := 0; i < length; i++ {
|
||||
values = append(values, c.formats[i])
|
||||
}
|
||||
values = append(values, c.level+".log")
|
||||
filename := filepath.Join(values...)
|
||||
director := filepath.Dir(filename)
|
||||
err = os.MkdirAll(director, os.ModePerm)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = removeNDaysFolders(c.director, c.retentionDay)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
c.file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return c.file.Write(bytes)
|
||||
}
|
||||
|
||||
func (c *Cutter) Sync() error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.file != nil {
|
||||
return c.file.Sync()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 增加日志目录文件清理 小于等于零的值默认忽略不再处理
|
||||
func removeNDaysFolders(dir string, days int) error {
|
||||
if days <= 0 {
|
||||
return nil
|
||||
}
|
||||
cutoff := time.Now().AddDate(0, 0, -days)
|
||||
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() && info.ModTime().Before(cutoff) && path != dir {
|
||||
err = os.RemoveAll(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ZapCore struct {
|
||||
level zapcore.Level
|
||||
zapcore.Core
|
||||
}
|
||||
|
||||
func NewZapCore(level zapcore.Level) *ZapCore {
|
||||
entity := &ZapCore{level: level}
|
||||
syncer := entity.WriteSyncer()
|
||||
levelEnabler := zap.LevelEnablerFunc(func(l zapcore.Level) bool {
|
||||
return l == level
|
||||
})
|
||||
entity.Core = zapcore.NewCore(global.GVA_CONFIG.Zap.Encoder(), syncer, levelEnabler)
|
||||
return entity
|
||||
}
|
||||
|
||||
func (z *ZapCore) WriteSyncer(formats ...string) zapcore.WriteSyncer {
|
||||
cutter := NewCutter(
|
||||
global.GVA_CONFIG.Zap.Director,
|
||||
z.level.String(),
|
||||
global.GVA_CONFIG.Zap.RetentionDay,
|
||||
CutterWithLayout(time.DateOnly),
|
||||
CutterWithFormats(formats...),
|
||||
)
|
||||
if global.GVA_CONFIG.Zap.LogInConsole {
|
||||
multiSyncer := zapcore.NewMultiWriteSyncer(os.Stdout, cutter)
|
||||
return zapcore.AddSync(multiSyncer)
|
||||
}
|
||||
return zapcore.AddSync(cutter)
|
||||
}
|
||||
|
||||
func (z *ZapCore) Enabled(level zapcore.Level) bool {
|
||||
return z.level == level
|
||||
}
|
||||
|
||||
func (z *ZapCore) With(fields []zapcore.Field) zapcore.Core {
|
||||
return z.Core.With(fields)
|
||||
}
|
||||
|
||||
func (z *ZapCore) Check(entry zapcore.Entry, check *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
||||
if z.Enabled(entry.Level) {
|
||||
return check.AddCore(entry, z)
|
||||
}
|
||||
return check
|
||||
}
|
||||
|
||||
func (z *ZapCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
|
||||
for i := 0; i < len(fields); i++ {
|
||||
if fields[i].Key == "business" || fields[i].Key == "folder" || fields[i].Key == "directory" {
|
||||
syncer := z.WriteSyncer(fields[i].String)
|
||||
z.Core = zapcore.NewCore(global.GVA_CONFIG.Zap.Encoder(), syncer, z.level)
|
||||
}
|
||||
}
|
||||
return z.Core.Write(entry, fields)
|
||||
}
|
||||
|
||||
func (z *ZapCore) Sync() error {
|
||||
return z.Core.Sync()
|
||||
}
|
||||
+26
-21
@@ -2,24 +2,28 @@ package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/initialize"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
type server interface {
|
||||
ListenAndServe() error
|
||||
}
|
||||
|
||||
func RunWindowsServer() {
|
||||
if global.GVA_CONFIG.System.UseMultipoint {
|
||||
func RunServer() {
|
||||
if global.GVA_CONFIG.System.UseRedis {
|
||||
// 初始化redis服务
|
||||
initialize.Redis()
|
||||
if global.GVA_CONFIG.System.UseMultipoint {
|
||||
initialize.RedisList()
|
||||
}
|
||||
}
|
||||
|
||||
if global.GVA_CONFIG.System.UseMongo {
|
||||
err := initialize.Mongo.Initialization()
|
||||
if err != nil {
|
||||
zap.L().Error(fmt.Sprintf("%+v", err))
|
||||
}
|
||||
}
|
||||
// 从db加载jwt数据
|
||||
if global.GVA_DB != nil {
|
||||
system.LoadAll()
|
||||
@@ -27,23 +31,24 @@ func RunWindowsServer() {
|
||||
|
||||
Router := initialize.Routers()
|
||||
|
||||
Router.Static("/form-generator", "./resource/page")
|
||||
|
||||
address := fmt.Sprintf(":%d", global.GVA_CONFIG.System.Addr)
|
||||
s := initServer(address, Router)
|
||||
// 保证文本顺序输出
|
||||
// In order to ensure that the text order output can be deleted
|
||||
time.Sleep(10 * time.Microsecond)
|
||||
global.GVA_LOG.Info("server run success on ", zap.String("address", address))
|
||||
|
||||
fmt.Printf(`
|
||||
欢迎使用 github.com/flipped-aurora/gin-vue-admin/server
|
||||
当前版本:V2.4.6
|
||||
加群方式:微信号:shouzi_1994 QQ群:622360840
|
||||
欢迎使用 gin-vue-admin
|
||||
当前版本:%s
|
||||
加群方式:微信号:shouzi_1994 QQ群:470239250
|
||||
项目地址:https://github.com/flipped-aurora/gin-vue-admin
|
||||
插件市场:https://plugin.gin-vue-admin.com
|
||||
GVA讨论社区:https://support.qq.com/products/371961
|
||||
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
|
||||
默认MCP SSE地址:http://127.0.0.1%s%s
|
||||
默认MCP Message地址:http://127.0.0.1%s%s
|
||||
默认前端文件运行地址:http://127.0.0.1:8080
|
||||
如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.github.com/flipped-aurora/gin-vue-admin/server.com/docs/coffee
|
||||
`, address)
|
||||
global.GVA_LOG.Error(s.ListenAndServe().Error())
|
||||
--------------------------------------版权声明--------------------------------------
|
||||
** 版权所有方:flipped-aurora开源团队 **
|
||||
** 版权持有公司:北京翻转极光科技有限责任公司 **
|
||||
** 剔除授权标识需购买商用授权:https://gin-vue-admin.com/empower/index.html **
|
||||
** 感谢您对Gin-Vue-Admin的支持与关注 合法授权使用更有利于项目的长久发展**
|
||||
`, global.Version, address, address, global.GVA_CONFIG.MCP.SSEPath, address, global.GVA_CONFIG.MCP.MessagePath)
|
||||
initServer(address, Router, 10*time.Minute, 10*time.Minute)
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/fvbock/endless"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func initServer(address string, router *gin.Engine) server {
|
||||
s := endless.NewServer(address, router)
|
||||
s.ReadHeaderTimeout = 20 * time.Second
|
||||
s.WriteTimeout = 20 * time.Second
|
||||
s.MaxHeaderBytes = 1 << 20
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type server interface {
|
||||
ListenAndServe() error
|
||||
Shutdown(context.Context) error
|
||||
}
|
||||
|
||||
// initServer 启动服务并实现优雅关闭
|
||||
func initServer(address string, router *gin.Engine, readTimeout, writeTimeout time.Duration) {
|
||||
// 创建服务
|
||||
srv := &http.Server{
|
||||
Addr: address,
|
||||
Handler: router,
|
||||
ReadTimeout: readTimeout,
|
||||
WriteTimeout: writeTimeout,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
}
|
||||
|
||||
// 在goroutine中启动服务
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
fmt.Printf("listen: %s\n", err)
|
||||
zap.L().Error("server启动失败", zap.Error(err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
// 等待中断信号以优雅地关闭服务器
|
||||
quit := make(chan os.Signal, 1)
|
||||
// kill (无参数) 默认发送 syscall.SIGTERM
|
||||
// kill -2 发送 syscall.SIGINT
|
||||
// kill -9 发送 syscall.SIGKILL,但是无法被捕获,所以不需要添加
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
zap.L().Info("关闭WEB服务...")
|
||||
|
||||
// 设置5秒的超时时间
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
|
||||
defer cancel()
|
||||
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
zap.L().Fatal("WEB服务关闭异常", zap.Error(err))
|
||||
}
|
||||
|
||||
zap.L().Info("WEB服务已关闭")
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func initServer(address string, router *gin.Engine) server {
|
||||
return &http.Server{
|
||||
Addr: address,
|
||||
Handler: router,
|
||||
ReadTimeout: 20 * time.Second,
|
||||
WriteTimeout: 20 * time.Second,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
}
|
||||
}
|
||||
+45
-35
@@ -5,62 +5,72 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/songzhibin97/gkit/cache/local_cache"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/core/internal"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
_ "github.com/flipped-aurora/gin-vue-admin/server/packfile"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func Viper(path ...string) *viper.Viper {
|
||||
var config string
|
||||
if len(path) == 0 {
|
||||
flag.StringVar(&config, "c", "", "choose config file.")
|
||||
flag.Parse()
|
||||
if config == "" { // 优先级: 命令行 > 环境变量 > 默认值
|
||||
if configEnv := os.Getenv(utils.ConfigEnv); configEnv == "" {
|
||||
config = utils.ConfigFile
|
||||
fmt.Printf("您正在使用config的默认值,config的路径为%v\n", utils.ConfigFile)
|
||||
} else {
|
||||
config = configEnv
|
||||
fmt.Printf("您正在使用GVA_CONFIG环境变量,config的路径为%v\n", config)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("您正在使用命令行的-c参数传递的值,config的路径为%v\n", config)
|
||||
}
|
||||
} else {
|
||||
config = path[0]
|
||||
fmt.Printf("您正在使用func Viper()传递的值,config的路径为%v\n", config)
|
||||
}
|
||||
// Viper 配置
|
||||
func Viper() *viper.Viper {
|
||||
config := getConfigPath()
|
||||
|
||||
v := viper.New()
|
||||
v.SetConfigFile(config)
|
||||
v.SetConfigType("yaml")
|
||||
err := v.ReadInConfig()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Fatal error config file: %s \n", err))
|
||||
panic(fmt.Errorf("fatal error config file: %w", err))
|
||||
}
|
||||
v.WatchConfig()
|
||||
|
||||
v.OnConfigChange(func(e fsnotify.Event) {
|
||||
fmt.Println("config file changed:", e.Name)
|
||||
if err := v.Unmarshal(&global.GVA_CONFIG); err != nil {
|
||||
if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
})
|
||||
if err := v.Unmarshal(&global.GVA_CONFIG); err != nil {
|
||||
fmt.Println(err)
|
||||
if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
|
||||
panic(fmt.Errorf("fatal error unmarshal config: %w", err))
|
||||
}
|
||||
// root 适配性
|
||||
// 根据root位置去找到对应迁移位置,保证root路径有效
|
||||
|
||||
// root 适配性 根据root位置去找到对应迁移位置,保证root路径有效
|
||||
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
|
||||
global.BlackCache = local_cache.NewCache(
|
||||
local_cache.SetDefaultExpire(time.Second * time.Duration(global.GVA_CONFIG.JWT.ExpiresTime)),
|
||||
)
|
||||
return v
|
||||
}
|
||||
|
||||
// getConfigPath 获取配置文件路径, 优先级: 命令行 > 环境变量 > 默认值
|
||||
func getConfigPath() (config string) {
|
||||
// `-c` flag parse
|
||||
flag.StringVar(&config, "c", "", "choose config file.")
|
||||
flag.Parse()
|
||||
if config != "" { // 命令行参数不为空 将值赋值于config
|
||||
fmt.Printf("您正在使用命令行的 '-c' 参数传递的值, config 的路径为 %s\n", config)
|
||||
return
|
||||
}
|
||||
if env := os.Getenv(internal.ConfigEnv); env != "" { // 判断环境变量 GVA_CONFIG
|
||||
config = env
|
||||
fmt.Printf("您正在使用 %s 环境变量, config 的路径为 %s\n", internal.ConfigEnv, config)
|
||||
return
|
||||
}
|
||||
|
||||
switch gin.Mode() { // 根据 gin 模式文件名
|
||||
case gin.DebugMode:
|
||||
config = internal.ConfigDebugFile
|
||||
case gin.ReleaseMode:
|
||||
config = internal.ConfigReleaseFile
|
||||
case gin.TestMode:
|
||||
config = internal.ConfigTestFile
|
||||
}
|
||||
fmt.Printf("您正在使用 gin 的 %s 模式运行, config 的路径为 %s\n", gin.Mode(), config)
|
||||
|
||||
_, err := os.Stat(config)
|
||||
if err != nil || os.IsNotExist(err) {
|
||||
config = internal.ConfigDefaultFile
|
||||
fmt.Printf("配置文件路径不存在, 使用默认配置文件路径: %s\n", config)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
+11
-76
@@ -2,96 +2,31 @@ package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/core/internal"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Zap 获取 zap.Logger
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func Zap() (logger *zap.Logger) {
|
||||
if ok, _ := utils.PathExists(global.GVA_CONFIG.Zap.Director); !ok { // 判断是否有Director文件夹
|
||||
fmt.Printf("create %v directory\n", global.GVA_CONFIG.Zap.Director)
|
||||
_ = os.Mkdir(global.GVA_CONFIG.Zap.Director, os.ModePerm)
|
||||
}
|
||||
// 调试级别
|
||||
debugPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
|
||||
return lev == zap.DebugLevel
|
||||
})
|
||||
// 日志级别
|
||||
infoPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
|
||||
return lev == zap.InfoLevel
|
||||
})
|
||||
// 警告级别
|
||||
warnPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
|
||||
return lev == zap.WarnLevel
|
||||
})
|
||||
// 错误级别
|
||||
errorPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
|
||||
return lev >= zap.ErrorLevel
|
||||
})
|
||||
|
||||
cores := [...]zapcore.Core{
|
||||
getEncoderCore(fmt.Sprintf("./%s/server_debug.log", global.GVA_CONFIG.Zap.Director), debugPriority),
|
||||
getEncoderCore(fmt.Sprintf("./%s/server_info.log", global.GVA_CONFIG.Zap.Director), infoPriority),
|
||||
getEncoderCore(fmt.Sprintf("./%s/server_warn.log", global.GVA_CONFIG.Zap.Director), warnPriority),
|
||||
getEncoderCore(fmt.Sprintf("./%s/server_error.log", global.GVA_CONFIG.Zap.Director), errorPriority),
|
||||
levels := global.GVA_CONFIG.Zap.Levels()
|
||||
length := len(levels)
|
||||
cores := make([]zapcore.Core, 0, length)
|
||||
for i := 0; i < length; i++ {
|
||||
core := internal.NewZapCore(levels[i])
|
||||
cores = append(cores, core)
|
||||
}
|
||||
logger = zap.New(zapcore.NewTee(cores[:]...), zap.AddCaller())
|
||||
|
||||
logger = zap.New(zapcore.NewTee(cores...))
|
||||
if global.GVA_CONFIG.Zap.ShowLine {
|
||||
logger = logger.WithOptions(zap.AddCaller())
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
// getEncoderConfig 获取zapcore.EncoderConfig
|
||||
func getEncoderConfig() (config zapcore.EncoderConfig) {
|
||||
config = zapcore.EncoderConfig{
|
||||
MessageKey: "message",
|
||||
LevelKey: "level",
|
||||
TimeKey: "time",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
StacktraceKey: global.GVA_CONFIG.Zap.StacktraceKey,
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||
EncodeTime: CustomTimeEncoder,
|
||||
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||
EncodeCaller: zapcore.FullCallerEncoder,
|
||||
}
|
||||
switch {
|
||||
case global.GVA_CONFIG.Zap.EncodeLevel == "LowercaseLevelEncoder": // 小写编码器(默认)
|
||||
config.EncodeLevel = zapcore.LowercaseLevelEncoder
|
||||
case global.GVA_CONFIG.Zap.EncodeLevel == "LowercaseColorLevelEncoder": // 小写编码器带颜色
|
||||
config.EncodeLevel = zapcore.LowercaseColorLevelEncoder
|
||||
case global.GVA_CONFIG.Zap.EncodeLevel == "CapitalLevelEncoder": // 大写编码器
|
||||
config.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||
case global.GVA_CONFIG.Zap.EncodeLevel == "CapitalColorLevelEncoder": // 大写编码器带颜色
|
||||
config.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
default:
|
||||
config.EncodeLevel = zapcore.LowercaseLevelEncoder
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// getEncoder 获取zapcore.Encoder
|
||||
func getEncoder() zapcore.Encoder {
|
||||
if global.GVA_CONFIG.Zap.Format == "json" {
|
||||
return zapcore.NewJSONEncoder(getEncoderConfig())
|
||||
}
|
||||
return zapcore.NewConsoleEncoder(getEncoderConfig())
|
||||
}
|
||||
|
||||
// getEncoderCore 获取Encoder的zapcore.Core
|
||||
func getEncoderCore(fileName string, level zapcore.LevelEnabler) (core zapcore.Core) {
|
||||
writer := utils.GetWriteSyncer(fileName) // 使用file-rotatelogs进行日志分割
|
||||
return zapcore.NewCore(getEncoder(), writer, level)
|
||||
}
|
||||
|
||||
// 自定义日志输出时间格式
|
||||
func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||
enc.AppendString(t.Format(global.GVA_CONFIG.Zap.Prefix + "2006/01/02 - 15:04:05.000"))
|
||||
}
|
||||
|
||||
+5441
-1004
File diff suppressed because it is too large
Load Diff
+5430
-958
File diff suppressed because it is too large
Load Diff
+3335
-729
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user