Compare commits
911 Commits
tag-regist
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e356abf1e | ||
|
|
7599c51f80 | ||
|
|
c2f3afdffa | ||
|
|
922b00c027 | ||
|
|
e68ff2dcfd | ||
|
|
b9c58b328b | ||
|
|
5001809fef | ||
|
|
9d8a04b702 | ||
|
|
6310d895cb | ||
|
|
e0f64e43c5 | ||
|
|
5a042175be | ||
|
|
51dd0faced | ||
|
|
77c92c646e | ||
|
|
f5d0c9d4b4 | ||
|
|
6d1430c8ad | ||
|
|
5fa5e76f61 | ||
|
|
fd4ff7c98d | ||
|
|
0588006cd1 | ||
|
|
933612bad3 | ||
|
|
c8ae961b0e | ||
|
|
8cbf74d53a | ||
|
|
1d18e1b7d3 | ||
|
|
71e22bc4ae | ||
|
|
f908af2323 | ||
|
|
4d70c5167d | ||
|
|
c18ee0dff9 | ||
|
|
c357202e53 | ||
|
|
49283557c2 | ||
|
|
fe0c08ad75 | ||
|
|
3643466507 | ||
|
|
a162db3354 | ||
|
|
d3d0eb4b1b | ||
|
|
f8350fe357 | ||
|
|
be2af14104 | ||
|
|
c7c7e4cf98 | ||
|
|
cd6c85a9c4 | ||
|
|
acabd2ed70 | ||
|
|
20851cfbda | ||
|
|
1abc277f40 | ||
|
|
d82dd73099 | ||
|
|
30c7245cbb | ||
|
|
c5f4e06df1 | ||
|
|
5acf4f85b0 | ||
|
|
9106daf96c | ||
|
|
8c5b0e2349 | ||
|
|
7ffb3b8d9c | ||
|
|
2724e34dc9 | ||
|
|
3a19208442 | ||
|
|
1f2ab33ba3 | ||
|
|
01b538366d | ||
|
|
df51be3472 | ||
|
|
ff382b86b7 | ||
|
|
e5e1e00b9c | ||
|
|
46110d9f4f | ||
|
|
e4a7791fd2 | ||
|
|
8dd6b66951 | ||
|
|
a748399e41 | ||
|
|
b0a68908bd | ||
|
|
c2664c4e37 | ||
|
|
96a37e65be | ||
|
|
b0dadbaa1f | ||
|
|
3213a634c5 | ||
|
|
07c21c426a | ||
|
|
914adf6918 | ||
|
|
09eef2f049 | ||
|
|
bb132abe28 | ||
|
|
e3e6ae4d7f | ||
|
|
26da6bf754 | ||
|
|
6c70e9a5d2 | ||
|
|
7786d72ea3 | ||
|
|
8515f26b24 | ||
|
|
33b362befb | ||
|
|
dc6ca2854e | ||
|
|
71b064d607 | ||
|
|
be50186da6 | ||
|
|
ab4fb5a914 | ||
|
|
1fa806fcd5 | ||
|
|
f9a663d560 | ||
|
|
a2a438562b | ||
|
|
1cecae512c | ||
|
|
a5dbf71513 | ||
|
|
aa8f044c70 | ||
|
|
e09c0106a0 | ||
|
|
10b060255c | ||
|
|
ddc709ede4 | ||
|
|
1f04edeaff | ||
|
|
473ee11323 | ||
|
|
aaf978bd0d | ||
|
|
72f5420d4f | ||
|
|
7bc96d690b | ||
|
|
5868708065 | ||
|
|
92b2d6443b | ||
|
|
b4e6e3f94d | ||
|
|
5bd0670389 | ||
|
|
e4e5cb0496 | ||
|
|
c18c05e019 | ||
|
|
3e900cf0fe | ||
|
|
40865ab049 | ||
|
|
9e4c9e02ff | ||
|
|
bf6af442e0 | ||
|
|
b9d5875847 | ||
|
|
150379c5f9 | ||
|
|
054560ae64 | ||
|
|
091482f9cc | ||
|
|
5e25555404 | ||
|
|
ff5dc5204d | ||
|
|
5d9fbc4ef9 | ||
|
|
e009576889 | ||
|
|
d435015ba9 | ||
|
|
12479cffd2 | ||
|
|
3f7bb181fa | ||
|
|
7d9a37edf4 | ||
|
|
c77312b178 | ||
|
|
4ab34db4fe | ||
|
|
552875289e | ||
|
|
47f061fd43 | ||
|
|
403e8111f4 | ||
|
|
02fb5a9e85 | ||
|
|
bb2a9eb314 | ||
|
|
f7f04c28eb | ||
|
|
3e06fcd799 | ||
|
|
28808ab1e3 | ||
|
|
960b83c916 | ||
|
|
91e3420b6f | ||
|
|
6ad3b309fb | ||
|
|
bd72c334cb | ||
|
|
c407261bd7 | ||
|
|
77b52508bb | ||
|
|
d70800d0e1 | ||
|
|
1ce9ebbff9 | ||
|
|
942826f4b0 | ||
|
|
c0cfdbafd9 | ||
|
|
09330a7431 | ||
|
|
a9598f6e0a | ||
|
|
29f8a95833 | ||
|
|
85b33e7c3d | ||
|
|
5e2c3fab46 | ||
|
|
ba384660f1 | ||
|
|
16a2ca7723 | ||
|
|
eefddbe5c0 | ||
|
|
3db05e8bdd | ||
|
|
00f639ba19 | ||
|
|
5f04df2ce3 | ||
|
|
64637e6dfe | ||
|
|
7c5c47153e | ||
|
|
1ee4b2384c | ||
|
|
0498d0f7b5 | ||
|
|
9e05259d92 | ||
|
|
5956af3564 | ||
|
|
f72fd07ea4 | ||
|
|
9b2dc49a03 | ||
|
|
21b427f116 | ||
|
|
a88bb80717 | ||
|
|
53a40a1cb4 | ||
|
|
d9a257ff23 | ||
|
|
7334fad2ab | ||
|
|
a598bf3404 | ||
|
|
f76eab3c30 | ||
|
|
0e29510d0e | ||
|
|
08f708d8a4 | ||
|
|
d3b80f63b8 | ||
|
|
a690c4a72d | ||
|
|
40835d2f69 | ||
|
|
13b06f0592 | ||
|
|
dabf8032ca | ||
|
|
8b34c0a80d | ||
|
|
b01f16d6df | ||
|
|
054c86a450 | ||
|
|
d06a13df4a | ||
|
|
d32c9616c6 | ||
|
|
483e3969be | ||
|
|
89304e8004 | ||
|
|
c84a05b522 | ||
|
|
0cbadbeda8 | ||
|
|
17bc2d5ca8 | ||
|
|
35b3e6489d | ||
|
|
cff2ee120b | ||
|
|
dd11ab46be | ||
|
|
e23911b075 | ||
|
|
d9fb25dc0a | ||
|
|
c08fec74c9 | ||
|
|
00da3cc0a9 | ||
|
|
89ce369c8f | ||
|
|
50e576b9fc | ||
|
|
a14ac9e3c9 | ||
|
|
95072a02f6 | ||
|
|
cccafde158 | ||
|
|
d4823ae473 | ||
|
|
5e8a5d7133 | ||
|
|
9a213743d7 | ||
|
|
5dd9c597a9 | ||
|
|
7f6b3a4309 | ||
|
|
0831ddd073 | ||
|
|
6053a3f0ec | ||
|
|
19022bdbe1 | ||
|
|
c7386f5f66 | ||
|
|
7b930fa4b8 | ||
|
|
a4ad90c90e | ||
|
|
f974d75f45 | ||
|
|
ba61d9dd58 | ||
|
|
5a2c15a835 | ||
|
|
fde8e73bdc | ||
|
|
b1e346d48d | ||
|
|
98b78339ab | ||
|
|
08d7745f4e | ||
|
|
f0c01dfd16 | ||
|
|
66f5493e52 | ||
|
|
efe46ff3e1 | ||
|
|
a16a7aa2d3 | ||
|
|
f8b8e5fca1 | ||
|
|
06f400fb81 | ||
|
|
0e20e75752 | ||
|
|
1ef78cf582 | ||
|
|
9ca2488f73 | ||
|
|
e1cc2c982d | ||
|
|
0b609305b1 | ||
|
|
4eae92b033 | ||
|
|
3ceb8cdc93 | ||
|
|
fc82fab1cf | ||
|
|
c573a5118d | ||
|
|
2d6370ba1b | ||
|
|
f09e00ddd7 | ||
|
|
ccca3c26ee | ||
|
|
764d492449 | ||
|
|
4fe912ab58 | ||
|
|
2d8948fd5a | ||
|
|
2150d499bb | ||
|
|
4055210942 | ||
|
|
7819f21fbf | ||
|
|
aaef05525e | ||
|
|
e1b860c75a | ||
|
|
472942e93e | ||
|
|
a1fdf71f3e | ||
|
|
bf977b2218 | ||
|
|
c8fb583c7a | ||
|
|
412a061668 | ||
|
|
9c3bafdd92 | ||
|
|
3804bf33a7 | ||
|
|
31202908a4 | ||
|
|
ef0c09cb02 | ||
|
|
7ec542abce | ||
|
|
8543754888 | ||
|
|
f753faed66 | ||
|
|
842f3a2ae5 | ||
|
|
8aeb6c16bf | ||
|
|
9058022933 | ||
|
|
66f88ddf30 | ||
|
|
307d63e21e | ||
|
|
1b938b6ac2 | ||
|
|
0430bb9bf6 | ||
|
|
df8368cafc | ||
|
|
c38939ae4b | ||
|
|
66e5276360 | ||
|
|
9188916926 | ||
|
|
8cdb1fd031 | ||
|
|
bf022b2da2 | ||
|
|
7dccb19161 | ||
|
|
de22c333d0 | ||
|
|
db9a2f4e23 | ||
|
|
1b19b65d81 | ||
|
|
c05f08cc36 | ||
|
|
1f1509f6db | ||
|
|
581b4c869b | ||
|
|
3fb6ac30e6 | ||
|
|
3379ae1020 | ||
|
|
f3ade3e7db | ||
|
|
f32eccd492 | ||
|
|
a01a6e88d3 | ||
|
|
008c0f830f | ||
|
|
73fbd7a072 | ||
|
|
888825ba50 | ||
|
|
d0fe92a96c | ||
|
|
995d5ba623 | ||
|
|
181b881bc5 | ||
|
|
91f8c82f0e | ||
|
|
5d00092cc5 | ||
|
|
2c765af036 | ||
|
|
d223659376 | ||
|
|
419f041786 | ||
|
|
7bae549b30 | ||
|
|
4b135a62dd | ||
|
|
a9a3dcdd0a | ||
|
|
a577df49c1 | ||
|
|
306742b72f | ||
|
|
43bd300f4a | ||
|
|
c475a1b8cd | ||
|
|
5af379160c | ||
|
|
0f5f659b48 | ||
|
|
2f707cc99d | ||
|
|
8efd1fc7b4 | ||
|
|
e09f332fe6 | ||
|
|
d642a4f9c6 | ||
|
|
8d071e2ac3 | ||
|
|
a3d97a4f8c | ||
|
|
b053c7b4e5 | ||
|
|
883a6afd0b | ||
|
|
14e0383617 | ||
|
|
32425e7903 | ||
|
|
db384fe8a0 | ||
|
|
9fa4ccce2b | ||
|
|
4451918acb | ||
|
|
f12b6e7b01 | ||
|
|
1ff4303d2f | ||
|
|
dee635ba6c | ||
|
|
44e321720a | ||
|
|
ced55fb3b2 | ||
|
|
950e8a53c3 | ||
|
|
e40d885078 | ||
|
|
3fce909a35 | ||
|
|
eecbce1e4d | ||
|
|
019fa9db09 | ||
|
|
e92f7c480d | ||
|
|
f59223b36e | ||
|
|
37f61700c1 | ||
|
|
354d9c70b0 | ||
|
|
3d68ea80cd | ||
|
|
ae717f094f | ||
|
|
c0e165f59b | ||
|
|
376c5179b2 | ||
|
|
01937163b3 | ||
|
|
4e6de894f0 | ||
|
|
a79272df2d | ||
|
|
47b7037a12 | ||
|
|
32c3925986 | ||
|
|
bdd02abda2 | ||
|
|
6bc596efe1 | ||
|
|
d3bb2a6c29 | ||
|
|
35161c300a | ||
|
|
503631fcbe | ||
|
|
413327df63 | ||
|
|
63d9033858 | ||
|
|
4b1fa84f36 | ||
|
|
eeda6b36f2 | ||
|
|
3247f4528a | ||
|
|
b3aa515d01 | ||
|
|
35564e66d8 | ||
|
|
e1b287240e | ||
|
|
1203aaf246 | ||
|
|
3ab4e70be9 | ||
|
|
a9a17731b2 | ||
|
|
d5d987c3d2 | ||
|
|
13e30c87b6 | ||
|
|
2243a93f5b | ||
|
|
7f6eee4c07 | ||
|
|
bf42779369 | ||
|
|
3a8be78532 | ||
|
|
d76a2d7b68 | ||
|
|
973c8a142d | ||
|
|
326eba4d5a | ||
|
|
309707f4a3 | ||
|
|
84856e6865 | ||
|
|
ae3ffd4f39 | ||
|
|
832c6cb5fd | ||
|
|
eb7f9d69c0 | ||
|
|
a79eeb0208 | ||
|
|
1f9b82776d | ||
|
|
27ec6c5915 | ||
|
|
2b08d4d9f2 | ||
|
|
307065dded | ||
|
|
6ab696dfaf | ||
|
|
1370883af9 | ||
|
|
c49b6eecaa | ||
|
|
8c8b1c7442 | ||
|
|
ad16abe9f4 | ||
|
|
ca6021a67c | ||
|
|
287dba9c89 | ||
|
|
80003c44d3 | ||
|
|
4ce2fc826d | ||
|
|
dc584c36d6 | ||
|
|
6f4c635855 | ||
|
|
8e833c295f | ||
|
|
3cef44dc96 | ||
|
|
5e906bc4ba | ||
|
|
446b98ea7a | ||
|
|
06dbb21796 | ||
|
|
80cc48b3b1 | ||
|
|
4c552199fb | ||
|
|
70b8525ccc | ||
|
|
9dfaca5c6f | ||
|
|
3b1b1845f8 | ||
|
|
fc8f305e3d | ||
|
|
a360fe713a | ||
|
|
a94b8e2770 | ||
|
|
dc8d28a256 | ||
|
|
418c9ffc7a | ||
|
|
02d3955d00 | ||
|
|
3b2a7f92d7 | ||
|
|
71d420c22e | ||
|
|
ace5e1d5f6 | ||
|
|
3c55c17818 | ||
|
|
62bbb9a130 | ||
|
|
92d2ba6a20 | ||
|
|
9f83da1cda | ||
|
|
58d42dc382 | ||
|
|
645a14e4f2 | ||
|
|
dc24e511a1 | ||
|
|
34fed4b924 | ||
|
|
c38c31031a | ||
|
|
b4a805ccef | ||
|
|
c4194817d5 | ||
|
|
e661f45bd1 | ||
|
|
5e081b5cfd | ||
|
|
3ea29e8bf0 | ||
|
|
c2489e4d7e | ||
|
|
af69ccb730 | ||
|
|
9ea5626074 | ||
|
|
65342b54ee | ||
|
|
98f2900e13 | ||
|
|
676771f5d0 | ||
|
|
0c4dd04bc7 | ||
|
|
1850d6d567 | ||
|
|
d0d4ab09f2 | ||
|
|
ad742128b7 | ||
|
|
87a2a3a629 | ||
|
|
994960da31 | ||
|
|
faa9310759 | ||
|
|
3ca80dd6a6 | ||
|
|
2eca422224 | ||
|
|
c9b6f73a46 | ||
|
|
636bbf20df | ||
|
|
4ed9da7707 | ||
|
|
f66d8bed59 | ||
|
|
95f2ecf7dc | ||
|
|
c6e5a20a81 | ||
|
|
01bb3eae06 | ||
|
|
c318534922 | ||
|
|
9c507c8bd3 | ||
|
|
725e08d8e7 | ||
|
|
066e6a7d57 | ||
|
|
2270c50886 | ||
|
|
3f9365f5d5 | ||
|
|
8410b90001 | ||
|
|
6e54384571 | ||
|
|
f223a56fd5 | ||
|
|
8e8f24f5e8 | ||
|
|
ebae72d883 | ||
|
|
4fad0d67ef | ||
|
|
34519ee280 | ||
|
|
bdf3334732 | ||
|
|
7468ce3524 | ||
|
|
45ceebc34b | ||
|
|
25fa143dda | ||
|
|
f6f412d102 | ||
|
|
77ecbb668c | ||
|
|
e86296ad5b | ||
|
|
4d260c72ab | ||
|
|
27d5b0135c | ||
|
|
a22bfb84db | ||
|
|
9e905666c8 | ||
|
|
5aa11c734e | ||
|
|
43e5707c01 | ||
|
|
1ac069fae8 | ||
|
|
7dab8da699 | ||
|
|
160e57df0e | ||
|
|
42979771ee | ||
|
|
315177677c | ||
|
|
f1d8cc8318 | ||
|
|
49cd8f7fda | ||
|
|
8239a536b9 | ||
|
|
c8598b5a0e | ||
|
|
67a3119e5d | ||
|
|
8a87e945de | ||
|
|
5b58f110a2 | ||
|
|
a20c3fa14c | ||
|
|
199cb67fa3 | ||
|
|
57c8f34c3b | ||
|
|
4456d15182 | ||
|
|
e163dbc2a8 | ||
|
|
c62f13e897 | ||
|
|
4d6693aca2 | ||
|
|
f86e569c03 | ||
|
|
ab56f20939 | ||
|
|
71bcf2da1b | ||
|
|
8e0677a86e | ||
|
|
294b58a7f6 | ||
|
|
4c1197e181 | ||
|
|
33e50a1fae | ||
|
|
0fea955db9 | ||
|
|
ee30f18d53 | ||
|
|
a68ac1a10c | ||
|
|
6df91ddfda | ||
|
|
57d0ccbede | ||
|
|
c120788857 | ||
|
|
d4225aff9d | ||
|
|
cc8bb34bb0 | ||
|
|
06229d6b90 | ||
|
|
a90d0214a8 | ||
|
|
f2e6ea6201 | ||
|
|
e8b251ddb8 | ||
|
|
acb259fe6f | ||
|
|
a1f2954544 | ||
|
|
2d10d58935 | ||
|
|
3118f07bbc | ||
|
|
6406f023db | ||
|
|
7f83dbe5b0 | ||
|
|
f66e2f8891 | ||
|
|
04a1a05127 | ||
|
|
2588014b6e | ||
|
|
70a23d1d44 | ||
|
|
d943377c00 | ||
|
|
007c52b174 | ||
|
|
131e7b9403 | ||
|
|
739545526e | ||
|
|
0fb884c451 | ||
|
|
5f0a1d1a43 | ||
|
|
45eae0c99f | ||
|
|
08d2371b2b | ||
|
|
46f8558c5a | ||
|
|
fe496a1dc3 | ||
|
|
7a0fa4c039 | ||
|
|
b2f6159f0c | ||
|
|
5f9bab5807 | ||
|
|
44f5506375 | ||
|
|
760adf0f6d | ||
|
|
fc7ddef575 | ||
|
|
33ae5013e9 | ||
|
|
43070efae9 | ||
|
|
e0249e5e6e | ||
|
|
801b5f1fd4 | ||
|
|
e8e34f0137 | ||
|
|
33b9c870bb | ||
|
|
7b8899abec | ||
|
|
53a995a5c5 | ||
|
|
8e20db4514 | ||
|
|
49c9572a3b | ||
|
|
eaecc720b3 | ||
|
|
026b8706e9 | ||
|
|
bd086e386a | ||
|
|
d0b8ff94d9 | ||
|
|
8e71da73ba | ||
|
|
d08ba46063 | ||
|
|
62261dc107 | ||
|
|
ad302514d4 | ||
|
|
beff70de45 | ||
|
|
56ec7cfc84 | ||
|
|
145f683ad7 | ||
|
|
68e9f173f2 | ||
|
|
b76684c3c6 | ||
|
|
6a93e325f4 | ||
|
|
8de9db5ff1 | ||
|
|
405c161c68 | ||
|
|
6acee19124 | ||
|
|
21374c1665 | ||
|
|
aeae182628 | ||
|
|
e7daa46005 | ||
|
|
1c4ff766df | ||
|
|
69cb993f6e | ||
|
|
089964af91 | ||
|
|
59d07d94f8 | ||
|
|
d3d53745f9 | ||
|
|
5c0a635a64 | ||
|
|
d7a089b1ca | ||
|
|
c3065adef1 | ||
|
|
4ba59e0ebe | ||
|
|
9b8f7be8dd | ||
|
|
7d2d182263 | ||
|
|
03b6ce2c70 | ||
|
|
8fe0951c02 | ||
|
|
7887d1ac62 | ||
|
|
65038a9438 | ||
|
|
250a9b61c5 | ||
|
|
f92318dc7a | ||
|
|
2a7835e290 | ||
|
|
99cb38eaa4 | ||
|
|
563d5adcc0 | ||
|
|
e8e6fc4ce4 | ||
|
|
fab1a88d78 | ||
|
|
09a25f9629 | ||
|
|
150fae4ae0 | ||
|
|
8991694971 | ||
|
|
40b4709b38 | ||
|
|
820909036b | ||
|
|
935bb2a454 | ||
|
|
d76c5a5963 | ||
|
|
7b7ccf0e08 | ||
|
|
554520d4b8 | ||
|
|
3a6ea57226 | ||
|
|
f6ec1a6654 | ||
|
|
58add8be82 | ||
|
|
626fd3a8e4 | ||
|
|
8537ea8a70 | ||
|
|
3bd540d98f | ||
|
|
6b505dfd8d | ||
|
|
c1849bcaf3 | ||
|
|
bba600a4f5 | ||
|
|
23bbf26db7 | ||
|
|
9bb6bcb9cb | ||
|
|
7ce714dc05 | ||
|
|
69131929c7 | ||
|
|
0d3489f17e | ||
|
|
e1cd5c2298 | ||
|
|
7d682587f2 | ||
|
|
0e686ef888 | ||
|
|
d704d6770f | ||
|
|
8b72108a58 | ||
|
|
5a773745f9 | ||
|
|
c12eac02e6 | ||
|
|
7d759024b7 | ||
|
|
0935a185fa | ||
|
|
1a787c182a | ||
|
|
06c800cbdd | ||
|
|
b107886ed9 | ||
|
|
744e28b343 | ||
|
|
59289cb170 | ||
|
|
e995a2a64f | ||
|
|
8530867653 | ||
|
|
3d0a940b2c | ||
|
|
994b486163 | ||
|
|
57f272df94 | ||
|
|
f3575a6add | ||
|
|
0c7b1ae408 | ||
|
|
f068e6f941 | ||
|
|
ee710f8bf3 | ||
|
|
01b1e7b72d | ||
|
|
0958cf003c | ||
|
|
5fdddfd47c | ||
|
|
a12f08a485 | ||
|
|
0774e52cc9 | ||
|
|
0db69a88bf | ||
|
|
77e0db05be | ||
|
|
d505ad94ab | ||
|
|
43847a5a79 | ||
|
|
562b925ce5 | ||
|
|
3d906b5f7f | ||
|
|
3f8e2226a8 | ||
|
|
ca833a26e9 | ||
|
|
38d11f3486 | ||
|
|
f037fe23d8 | ||
|
|
eb6af6a97d | ||
|
|
3b60ee27b0 | ||
|
|
26cba8ff1e | ||
|
|
774d092465 | ||
|
|
dfcd4d42d7 | ||
|
|
93b48088e3 | ||
|
|
05395ea0b9 | ||
|
|
49615e6476 | ||
|
|
eaf501b3d0 | ||
|
|
5d02c8f608 | ||
|
|
fdd5d3b00d | ||
|
|
f2d3ee1af9 | ||
|
|
1532f3addc | ||
|
|
b03e908173 | ||
|
|
7fabddcd11 | ||
|
|
42ac4fff77 | ||
|
|
5576653ae5 | ||
|
|
8f9acdffac | ||
|
|
08ca6a2a59 | ||
|
|
888d1e0c4e | ||
|
|
a50f73563a | ||
|
|
bb3f45b197 | ||
|
|
0f0c1ec710 | ||
|
|
bddb668452 | ||
|
|
e1f9663487 | ||
|
|
1b3740a99a | ||
|
|
6818ed062d | ||
|
|
ef2cb5ccdd | ||
|
|
d6eef1218e | ||
|
|
2e88574284 | ||
|
|
baace8f53e | ||
|
|
7a3960117a | ||
|
|
75d5d9b101 | ||
|
|
94d9b0da90 | ||
|
|
3cf806cc90 | ||
|
|
2e853bb77c | ||
|
|
1077f57ff6 | ||
|
|
82b1ff61c8 | ||
|
|
cea8fbba7b | ||
|
|
8d63d865f6 | ||
|
|
95c577f2a5 | ||
|
|
6f9fff2b89 | ||
|
|
42abb033ab | ||
|
|
d193035d69 | ||
|
|
efe08c6389 | ||
|
|
0bdac8506b | ||
|
|
6a0bcf9061 | ||
|
|
f8ff5e7e8e | ||
|
|
64cb58a9ca | ||
|
|
f62475d3fc | ||
|
|
015b084855 | ||
|
|
48395c396c | ||
|
|
511370e9b3 | ||
|
|
4e8bdc4de4 | ||
|
|
5d2318f72d | ||
|
|
55c5d0cad9 | ||
|
|
f787bdfab7 | ||
|
|
eef82419c8 | ||
|
|
2b850929d9 | ||
|
|
2f8438ff09 | ||
|
|
ba6bf5f8e7 | ||
|
|
a08313ea81 | ||
|
|
530a92a3ea | ||
|
|
538452dbe6 | ||
|
|
8159c999af | ||
|
|
ec0cbfb87e | ||
|
|
58cd959e01 | ||
|
|
6b3b38e058 | ||
|
|
90b420e7d8 | ||
|
|
89be4288a7 | ||
|
|
f5b5a1286f | ||
|
|
3925568bea | ||
|
|
fe0d348b41 | ||
|
|
2f4c46dc16 | ||
|
|
78c93578d9 | ||
|
|
0cc8c0b991 | ||
|
|
caaff21876 | ||
|
|
18841b199e | ||
|
|
3871132b55 | ||
|
|
41386f6996 | ||
|
|
ae40a868d2 | ||
|
|
d85a09ef11 | ||
|
|
b4b48fc482 | ||
|
|
60bde25c23 | ||
|
|
a101b5e5a8 | ||
|
|
1c78bdcc02 | ||
|
|
f939fef1fa | ||
|
|
8f6a76f4e4 | ||
|
|
7c4a92a88c | ||
|
|
c014765c8d | ||
|
|
a65d9dd501 | ||
|
|
2e6dca1467 | ||
|
|
68e80ba984 | ||
|
|
c0fb37255a | ||
|
|
d6756b7f88 | ||
|
|
3e506b27ef | ||
|
|
b94b48eb60 | ||
|
|
ebae593ab6 | ||
|
|
77017cdd01 | ||
|
|
c5d36a8200 | ||
|
|
35253be77d | ||
|
|
be0adc0a8e | ||
|
|
51ca5ba319 | ||
|
|
bfe09e26a6 | ||
|
|
64c049912b | ||
|
|
fb7b659950 | ||
|
|
14cf242722 | ||
|
|
298a684bd6 | ||
|
|
6ab5b50a80 | ||
|
|
3dc11f0e20 | ||
|
|
0f04d86432 | ||
|
|
74966e1f91 | ||
|
|
fc1743242f | ||
|
|
1500237b6e | ||
|
|
7d2b7219be | ||
|
|
1cc0393043 | ||
|
|
20c0ba9724 | ||
|
|
3522269626 | ||
|
|
fcf4b6362d | ||
|
|
ab7b0bd4c0 | ||
|
|
5c77e0030c | ||
|
|
2698827ba0 | ||
|
|
99fd427eaa | ||
|
|
9d6d24bd5b | ||
|
|
c2b8c0a617 | ||
|
|
e3ff576268 | ||
|
|
cb03f5a554 | ||
|
|
d32d3931a1 | ||
|
|
b859e8b88f | ||
|
|
3021345dd6 | ||
|
|
e62c6ee9a3 | ||
|
|
d4db7242c3 | ||
|
|
d0eefd5ef0 | ||
|
|
ab8f87d043 | ||
|
|
603e2b7dc6 | ||
|
|
2626b05897 | ||
|
|
de50c4dd5a | ||
|
|
f6bdc25ea2 | ||
|
|
4827ff3a99 | ||
|
|
afe3ae59e4 | ||
|
|
c5d2b88922 | ||
|
|
5c1c174c8b | ||
|
|
097cc04514 | ||
|
|
6f880a3a51 | ||
|
|
36ee797ea6 | ||
|
|
4cbb92cf27 | ||
|
|
bdb4446758 | ||
|
|
a4f7527716 | ||
|
|
c68f071847 | ||
|
|
5b53d97b26 | ||
|
|
1b30e5ae97 | ||
|
|
efaf35ea7c | ||
|
|
5b2b9e4c57 | ||
|
|
e47cd0e002 | ||
|
|
0cafb5829b | ||
|
|
9fa4407091 | ||
|
|
77aea2256d | ||
|
|
c19686d003 | ||
|
|
ab884b3618 | ||
|
|
77f6b5e4a5 | ||
|
|
63775525cb | ||
|
|
ab21e1f9eb | ||
|
|
d4ec362a07 | ||
|
|
a994392e5f | ||
|
|
b32b2300b0 | ||
|
|
e101358673 | ||
|
|
9429d32f42 | ||
|
|
1ab8e22885 | ||
|
|
c240a6337a | ||
|
|
cf28d4eab4 | ||
|
|
ec32e8b39f | ||
|
|
84b31313bf | ||
|
|
d19e4a79e9 | ||
|
|
0ecc8cfb89 | ||
|
|
7e3c40ed92 | ||
|
|
3b1917428a | ||
|
|
84b3bcbb04 | ||
|
|
f123472912 | ||
|
|
691d642f26 | ||
|
|
6c9f5528f2 | ||
|
|
ba7f9a4e4d | ||
|
|
c217028521 | ||
|
|
2969f1738a | ||
|
|
cf3f3a7b0d | ||
|
|
045e8cdaba | ||
|
|
aaa5a86977 | ||
|
|
e839692da4 | ||
|
|
6408f0e7d2 | ||
|
|
0966784976 | ||
|
|
51c4c7a82d | ||
|
|
45f3eb8f89 | ||
|
|
3446a32bdf | ||
|
|
7d5d6ed3e3 | ||
|
|
90fdbbccf2 | ||
|
|
bbd4e55c6a | ||
|
|
555ba667bf | ||
|
|
a02ec8618c | ||
|
|
39c2f1a1c2 | ||
|
|
be4868ba01 | ||
|
|
693f069c48 | ||
|
|
bffc5b7e00 | ||
|
|
9caf70e267 | ||
|
|
1cae82176d | ||
|
|
299195e9a1 | ||
|
|
bb19a34444 | ||
|
|
b6c60f48a2 | ||
|
|
63ebdce4a8 | ||
|
|
75800d0ca2 | ||
|
|
c5c43b3be4 | ||
|
|
26f7d2843e | ||
|
|
9194482522 | ||
|
|
0496b70ec5 | ||
|
|
02c53920a4 | ||
|
|
ef51fe7667 | ||
|
|
e1bfbbb645 | ||
|
|
6d240ef243 | ||
|
|
6e2a0e6c13 | ||
|
|
90d6823693 | ||
|
|
d2fab766c4 | ||
|
|
c7451c84f9 | ||
|
|
bf4b920e17 | ||
|
|
219f944162 | ||
|
|
55e5a7e795 | ||
|
|
6fc6d9cea3 | ||
|
|
121c4da23b | ||
|
|
1717a8ab4f | ||
|
|
12a3cec8a3 | ||
|
|
16e56a93e3 | ||
|
|
d13fe50773 | ||
|
|
8e54d86606 | ||
|
|
a16e9df6a5 | ||
|
|
2f20184edd | ||
|
|
ae69b02a0f | ||
|
|
18fac20ffd | ||
|
|
9b8a176491 | ||
|
|
62f49eb404 | ||
|
|
3a2d591005 | ||
|
|
d166e425c0 | ||
|
|
0be8a83d07 | ||
|
|
6cca0a7e5e | ||
|
|
4bfc70065d | ||
|
|
68e0cc96c5 | ||
|
|
c5ce01208f | ||
|
|
734265601c | ||
|
|
f244575976 | ||
|
|
c263180812 | ||
|
|
d21bbfaa21 | ||
|
|
753fbc651a | ||
|
|
01c4fda639 | ||
|
|
2d5a65e525 | ||
|
|
47f621837a | ||
|
|
e5a49846ce | ||
|
|
49fb6bf15f | ||
|
|
6fc25b5434 | ||
|
|
05db438b9f | ||
|
|
7a7a3b7c8c | ||
|
|
3e49780fa2 | ||
|
|
8d9debbc2d | ||
|
|
fb4108e980 | ||
|
|
0f1c7e4d2a | ||
|
|
ef28db6a1d | ||
|
|
7c90239ef3 | ||
|
|
167d0a77f7 | ||
|
|
fce0b4d242 | ||
|
|
7a97b0ff9d | ||
|
|
306523511c | ||
|
|
d25e1de3d9 | ||
|
|
459f04a459 | ||
|
|
55cf0d5329 | ||
|
|
83627f1b2f | ||
|
|
9803c5133e | ||
|
|
bf5b1f37aa | ||
|
|
ee9d8ba240 | ||
|
|
d9bd18f72e | ||
|
|
0910f0500f | ||
|
|
e97facbc5b | ||
|
|
2dd0f19328 | ||
|
|
0341491dd5 | ||
|
|
adb9d9fcf1 | ||
|
|
dad2fa7de3 | ||
|
|
6360604879 | ||
|
|
0f46896306 | ||
|
|
baa6289aa3 |
17
.gitignore
vendored
Normal file → Executable file
@@ -1,4 +1,5 @@
|
||||
/target/
|
||||
target/
|
||||
logs/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### STS ###
|
||||
@@ -8,7 +9,6 @@
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
@@ -17,10 +17,9 @@
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
/build/
|
||||
|
||||
nbproject/private/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
/local-config/
|
||||
|
||||
14
Dockerfile
Executable file
@@ -0,0 +1,14 @@
|
||||
FROM java:8
|
||||
VOLUME /tmp
|
||||
VOLUME /sop
|
||||
|
||||
# 将所有应用放到一个镜像当中
|
||||
ADD sop-gateway/target/*.jar sop/sop-gateway/sop-gateway.jar
|
||||
ADD sop-admin/sop-admin-backend/backend-boot/target/*.jar sop/sop-admin/sop-admin.jar
|
||||
ADD sop-example/example-story/target/*.jar sop/sop-story/sop-story.jar
|
||||
|
||||
# 拷贝启动脚本
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
215
README.md
Normal file → Executable file
@@ -1,91 +1,190 @@
|
||||
# SOP(Simple Open Platform)
|
||||
|
||||
> 2.0版本正在开发中...
|
||||
**当前版本为5.0**
|
||||
|
||||
一个开放平台解决方案项目,基于Spring Cloud实现,目标是能够让用户快速得搭建起自己的开放平台。
|
||||
👉🏻 [开发文档](https://xcnm3jsc8anf.feishu.cn/wiki/IJygwHV5Wij9i9kZ2yEcTblbnPg)
|
||||
|
||||
---
|
||||
|
||||
一个开放平台解决方案项目,基于dubbo实现,目标让用户快速搭建自己的开放平台。
|
||||
|
||||
SOP提供了两种接口调用方式,分别是:[支付宝开放平台](https://docs.open.alipay.com/api)的调用方式和[淘宝开放平台](http://open.taobao.com/api.htm?docId=285&docType=2)的调用方式。
|
||||
通过简单的配置后,你的项目就具备了和支付宝开放平台的一样的接口提供能力。
|
||||
|
||||
SOP封装了开放平台大部分功能包括:签名验证、统一异常处理、统一返回内容 、业务参数验证(JSR-303)、秘钥管理等,未来还会实现更多功能。
|
||||
SOP封装了开放平台大部分功能包括:签名验证、统一异常处理、统一返回内容 、业务参数验证(JSR-303)、秘钥管理、接口回调等。
|
||||
|
||||
|
||||
## 项目特点
|
||||
|
||||
- 接入方式简单,与老项目不冲突,老项目注册到注册中心,然后在方法上加上注解即可。
|
||||
- 架构松耦合,业务代码实现在各自微服务上,SOP不参与业务实现,这也是Spring Cloud微服务体系带来的好处。
|
||||
- 扩展简单,开放平台对应的功能各自独立,可以自定义实现自己的需求,如:更改参数,更改签名规则等。
|
||||
+ 接入方式简单,与老项目不冲突,老项目注册到注册中心,然后在方法上加上注解即可。
|
||||
+ 架构松耦合,业务代码实现在各自微服务上,SOP不参与业务实现,这也是dubbo微服务体系带来的好处。
|
||||
+ 扩展简单,开放平台对应的功能各自独立,可以自定义实现自己的需求,如:更改参数,更改签名规则等。
|
||||
|
||||
## 谁可以使用这个项目
|
||||
|
||||
- 有现成的项目,想改造成开放平台供他人调用
|
||||
- 有现成的项目,想暴露其中几个接口并通过开放平台供他人调用
|
||||
- 想搭一个开放平台新项目,并结合微服务的方式去维护
|
||||
- 对开放平台感兴趣的朋友
|
||||
+ 有现成的项目,想改造成开放平台供他人调用
|
||||
+ 有现成的项目,想暴露其中几个接口并通过开放平台供他人调用
|
||||
+ 想搭一个开放平台新项目,并结合微服务的方式去维护
|
||||
+ 对开放平台感兴趣的朋友
|
||||
|
||||
以上情况都可以考虑使用SOP
|
||||
|
||||
## 架构图
|
||||
## 例子
|
||||
开放接口定义
|
||||
|
||||

|
||||
## 已完成列表
|
||||
```java
|
||||
/**
|
||||
* 支付接口
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
public interface OpenPayment {
|
||||
|
||||
- 签名验证
|
||||
- 统一异常处理
|
||||
- 统一返回内容
|
||||
- session管理
|
||||
- 秘钥管理
|
||||
- 微服务端自动验证(JSR-303)
|
||||
- 支持Spring Cloud Gateway
|
||||
- Admin管理平台,统一管理微服务配置,管理路由管理,微服务上下线
|
||||
- 接入方管理+秘钥管理
|
||||
- 接口权限分配
|
||||
- 文件上传
|
||||
- SDK
|
||||
- 接口限流
|
||||
- 文档整合
|
||||
- 应用授权
|
||||
- 监控日志
|
||||
- 支持nacos
|
||||
- 网关动态修改参数
|
||||
/**
|
||||
* 手机网站支付接口
|
||||
*
|
||||
* @apiNote 该接口是页面跳转接口,用于生成用户访问跳转链接。
|
||||
* 请在服务端执行SDK中pageExecute方法,读取响应中的body()结果。
|
||||
* 该结果用于跳转到页面,返回到用户浏览器渲染或重定向跳转到页面。
|
||||
* 具体使用方法请参考 <a href="https://torna.cn" target="_blank">接入指南</a>
|
||||
*/
|
||||
@Open("pay.trade.wap.pay")
|
||||
PayTradeWapPayResponse tradeWapPay(PayTradeWapPayRequest request);
|
||||
|
||||
## 界面预览
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
接口实现
|
||||
|
||||

|
||||
```java
|
||||
/**
|
||||
* 开放接口实现
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
@DubboService(validation = "true")
|
||||
public class OpenPaymentImpl implements OpenPayment {
|
||||
|
||||

|
||||
@Override
|
||||
public PayTradeWapPayResponse tradeWapPay(PayTradeWapPayRequest request) {
|
||||
PayTradeWapPayResponse payTradeWapPayResponse = new PayTradeWapPayResponse();
|
||||
payTradeWapPayResponse.setPageRedirectionData(UUID.randomUUID().toString());
|
||||
return payTradeWapPayResponse;
|
||||
}
|
||||
}
|
||||
|
||||

|
||||
```
|
||||
|
||||
## 工程说明
|
||||
调用:
|
||||
|
||||
> 运行环境:JDK8,Maven3,Zookeeper
|
||||
```java
|
||||
@Test
|
||||
public void testGet() throws Exception {
|
||||
// 公共请求参数
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("app_id", appId);
|
||||
params.put("method", "pay.trade.wap.pay");
|
||||
params.put("format", "json");
|
||||
params.put("charset", "utf-8");
|
||||
params.put("sign_type", "RSA2");
|
||||
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
|
||||
params.put("version", "1.0");
|
||||
|
||||
// 业务参数
|
||||
Map<String, Object> bizContent = new HashMap<>();
|
||||
bizContent.put("outTradeNo", "70501111111S001111119");
|
||||
bizContent.put("totalAmount", "9.00");
|
||||
bizContent.put("subject", "衣服");
|
||||
bizContent.put("productCode", "QUICK_WAP_WAY");
|
||||
|
||||
params.put("biz_content", JSON.toJSONString(bizContent));
|
||||
String content = AlipaySignature.getSignContent(params);
|
||||
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
|
||||
params.put("sign", sign);
|
||||
|
||||
System.out.println("----------- 请求信息 -----------");
|
||||
System.out.println("请求参数:" + buildParamQuery(params));
|
||||
System.out.println("商户秘钥:" + privateKey);
|
||||
System.out.println("待签名内容:" + content);
|
||||
System.out.println("签名(sign):" + sign);
|
||||
System.out.println("URL参数:" + buildUrlQuery(params));
|
||||
|
||||
System.out.println("----------- 返回结果 -----------");
|
||||
String responseData = postJson(url, params);// 发送请求
|
||||
System.out.println(responseData);
|
||||
}
|
||||
```
|
||||
|
||||
- doc:开发文档
|
||||
- sop-admin:后台管理
|
||||
- sop-registry:注册中心,eureka实现
|
||||
- sop-gateway:网关,统一访问入口,Spring Cloud Zuul实现,可切换成Spring Cloud Gateway
|
||||
- sop-common:公共模块,封装常用功能,包含签名校验、错误处理、限流等功能
|
||||
- sop-example:微服务示例,含springboot,springmvc示例
|
||||
- sop-sdk:基础sdk,含Java、C#版本
|
||||
- sop-test:接口调用测试用例
|
||||
- sop-website:开放平台对应网站,提供文档API、沙箱测试等内容
|
||||
SDK调用
|
||||
|
||||
```java
|
||||
@Test
|
||||
public void test() {
|
||||
PayTradeWapPayRequest request = new PayTradeWapPayRequest();
|
||||
|
||||
PayTradeWapPayModel model = new PayTradeWapPayModel();
|
||||
model.setOutTradeNo("70501111111S001111119");
|
||||
model.setTotalAmount(new BigDecimal("1000"));
|
||||
model.setSubject("衣服");
|
||||
model.setProductCode("QUICK_WAP_WAY");
|
||||
|
||||
request.setBizModel(model);
|
||||
|
||||
Result<PayTradeWapPayResponse> result = client.execute(request);
|
||||
if (result.isSuccess()) {
|
||||
PayTradeWapPayResponse response = result.getData();
|
||||
System.out.println(response);
|
||||
} else {
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 功能实现
|
||||
|
||||
- [x] 签名验签
|
||||
- [x] 秘钥管理
|
||||
- [x] 国际化
|
||||
- [x] 异常处理
|
||||
- [x] 文件上传/下载
|
||||
- [x] 文档管理
|
||||
- [x] 回调处理
|
||||
- [x] 自定义拦截器
|
||||
- [x] SDK
|
||||
- [x] C++ SDK
|
||||
- [x] C# SDK
|
||||
- [x] Go SDK
|
||||
- [x] Java SDK
|
||||
- [x] Nodejs SDK
|
||||
- [x] Python SDK
|
||||
- [x] Rust SDK
|
||||
|
||||
## 整体架构
|
||||

|
||||
|
||||
## 分支说明
|
||||
|
||||
- develop:日常开发分支
|
||||
- registry-nacos:nacos作为注册中心
|
||||
- SpringCloudGateway:SpringCloudGateway作为网关
|
||||
- master:发布分支
|
||||
- develop:开发分支,从master衍生
|
||||
- pr:接收PR合并分支,从master衍生
|
||||
|
||||
## 相关文档
|
||||
## 页面预览
|
||||
|
||||
[开发文档](http://durcframework.gitee.io/sop)
|
||||
- 文档页面
|
||||
|
||||
<img src="./asset/sop_doc.png" />
|
||||
|
||||
- 后台管理-文档管理
|
||||
|
||||
<img src="./asset/sop_admin_doc.jpg" />
|
||||
|
||||
- 后台管理-秘钥管理
|
||||
|
||||
<img src="./asset/sop_admin_secret.jpg" />
|
||||
|
||||
- 后台管理-用户管理
|
||||
|
||||
<img src="./asset/sop_admin_user.jpg" />
|
||||
|
||||
## 沟通交流
|
||||
|
||||
Q群:328419269
|
||||
<img src="./asset/sop_qqgroup.jpg" width="50%" height="50%" />
|
||||
|
||||
BIN
asset/arc.jpg
Executable file
|
After Width: | Height: | Size: 72 KiB |
BIN
asset/sop_admin_doc.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
asset/sop_admin_secret.jpg
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
asset/sop_admin_user.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
asset/sop_doc.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
asset/sop_qqgroup.jpg
Normal file
|
After Width: | Height: | Size: 433 KiB |
51
build-admin.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
# 构建admin, 结果输出在dist/sop-admin目录
|
||||
|
||||
# 获取当前路径并赋值给变量 current_path
|
||||
current_path=$(pwd)
|
||||
|
||||
# 打印变量的值,以验证赋值是否成功
|
||||
echo "当前路径是: $current_path"
|
||||
|
||||
# 构建目录
|
||||
dist_dir="dist"
|
||||
# 服务端文件夹名称
|
||||
# 执行文件名称
|
||||
app_name="sop-admin"
|
||||
version="5.0"
|
||||
build_folder="${app_name}-${version}"
|
||||
# 输出目录
|
||||
target_dir="$dist_dir/${build_folder}"
|
||||
|
||||
server_source=sop-admin/sop-admin-backend/admin-boot
|
||||
# admin前端路径
|
||||
front_source=sop-admin/sop-admin-frontend
|
||||
|
||||
# ------ 构建前端 ------
|
||||
echo "开始构建sop-admin前端..."
|
||||
cd $front_source
|
||||
sh build.sh
|
||||
cd $current_path
|
||||
|
||||
|
||||
# ------ 构建后端 ------
|
||||
echo "开始构建sop-admin服务端..."
|
||||
|
||||
mvn clean package -pl $server_source -am -DskipTests
|
||||
|
||||
# ------ 复制文件 ------
|
||||
if [ ! -d "$target_dir" ]; then
|
||||
mkdir -p $target_dir
|
||||
fi
|
||||
|
||||
rm -rf ${target_dir}/*
|
||||
|
||||
# 复制前端资源
|
||||
echo "复制前端文件到$target_dir"
|
||||
cp -r ${front_source}/dist ./$target_dir
|
||||
|
||||
# 复制服务端资源
|
||||
cp -r ${server_source}/target/*.jar $target_dir
|
||||
cp -r script/* $target_dir
|
||||
cp -r ${server_source}/src/main/resources/application-test.properties $target_dir/application.properties
|
||||
|
||||
echo "服务端构建完毕,构建结果在${target_dir}文件夹下"
|
||||
39
build-gateway.sh
Normal file
@@ -0,0 +1,39 @@
|
||||
# 构建网关
|
||||
|
||||
|
||||
# 获取当前路径并赋值给变量 current_path
|
||||
current_path=$(pwd)
|
||||
|
||||
# 打印变量的值,以验证赋值是否成功
|
||||
echo "当前路径是: $current_path"
|
||||
|
||||
# 构建目录
|
||||
dist_dir="dist"
|
||||
# 服务端文件夹名称
|
||||
# 执行文件名称
|
||||
app_name="sop-gateway"
|
||||
version="5.0"
|
||||
build_folder="${app_name}-${version}"
|
||||
# 输出目录
|
||||
target_dir="$dist_dir/${build_folder}"
|
||||
|
||||
server_source=sop-gateway
|
||||
|
||||
# ------ 构建后端 ------
|
||||
echo "开始构建sop-gateway..."
|
||||
|
||||
mvn clean package -pl $server_source -am -DskipTests
|
||||
|
||||
# ------ 复制文件 ------
|
||||
if [ ! -d "$target_dir" ]; then
|
||||
mkdir -p $target_dir
|
||||
fi
|
||||
|
||||
rm -rf ${target_dir}/*
|
||||
|
||||
# 复制服务端资源
|
||||
cp -r ${server_source}/target/*.jar $target_dir
|
||||
cp -r script/* $target_dir
|
||||
cp -r ${server_source}/src/main/resources/application-test.properties $target_dir/application.properties
|
||||
|
||||
echo "服务端构建完毕,构建结果在${target_dir}文件夹下"
|
||||
51
build-website.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
# 构建admin, 结果输出在dist/sop-admin目录
|
||||
|
||||
# 获取当前路径并赋值给变量 current_path
|
||||
current_path=$(pwd)
|
||||
|
||||
# 打印变量的值,以验证赋值是否成功
|
||||
echo "当前路径是: $current_path"
|
||||
|
||||
# 构建目录
|
||||
dist_dir="dist"
|
||||
# 服务端文件夹名称
|
||||
# 执行文件名称
|
||||
app_name="sop-website"
|
||||
version="5.0"
|
||||
build_folder="${app_name}-${version}"
|
||||
# 输出目录
|
||||
target_dir="$dist_dir/${build_folder}"
|
||||
|
||||
server_source=sop-website/sop-website-backend/website-boot
|
||||
# admin前端路径
|
||||
front_source=sop-website/sop-website-frontend
|
||||
|
||||
# ------ 构建前端 ------
|
||||
echo "开始构建sop-website前端..."
|
||||
cd $front_source
|
||||
sh build.sh
|
||||
cd $current_path
|
||||
|
||||
|
||||
# ------ 构建后端 ------
|
||||
echo "开始构建sop-website服务端..."
|
||||
|
||||
mvn clean package -pl $server_source -am -DskipTests
|
||||
|
||||
# ------ 复制文件 ------
|
||||
if [ ! -d "$target_dir" ]; then
|
||||
mkdir -p $target_dir
|
||||
fi
|
||||
|
||||
rm -rf ${target_dir}/*
|
||||
|
||||
# 复制前端资源
|
||||
echo "复制前端文件到$target_dir"
|
||||
cp -r ${front_source}/dist ./$target_dir
|
||||
|
||||
# 复制服务端资源
|
||||
cp -r ${server_source}/target/*.jar $target_dir
|
||||
cp -r script/* $target_dir
|
||||
cp -r ${server_source}/src/main/resources/application-test.properties $target_dir/application.properties
|
||||
|
||||
echo "服务端构建完毕,构建结果在${target_dir}文件夹下"
|
||||
175
changelog.md
Normal file → Executable file
@@ -1,152 +1,37 @@
|
||||
# changelog
|
||||
|
||||
## 1.15.2
|
||||
## 日常更新
|
||||
|
||||
- 优化SpringCloudGateway上传文件功能
|
||||
- 优化SpringCloudGateway动态修改参数功能
|
||||
- 2025-11-12:优化签名验证算法
|
||||
- 2025-11-05:添加SDK示例,返回List。详见:com.gitee.sop.sdk.SdkTest.testList
|
||||
- 2025-11-01:添加回调处理。有升级SQL,见:[sop-20251101.sql](./upgrade/sop-20251101.sql)
|
||||
- 2025-09-12:修复推送文档报找不到@Open注解问题
|
||||
- 2025-08-29:smart-doc升级到3.1.1
|
||||
- 2025-08-17:admin后台可关联商户;fastmybatis升级到3.1.7。有升级SQL,见:[sop-20250817.sql](./upgrade/sop-20250817.sql)
|
||||
- 2025-07-22: 修复当objClass被代理后,获取不到interface BUG
|
||||
- 2025-06-15:新增帮助文档管理。有升级SQL,见:[sop-20250615.sql](./upgrade/sop-20250615.sql)
|
||||
- 2025-06-11:java-sdk添加文件下载示例
|
||||
- 2025-06-01:OpenContext添加charset字段
|
||||
- 2025-05-18:修复网关registerAddress配置
|
||||
- 2025-05-11:
|
||||
- 修复:admin后台发布文档不生效问题
|
||||
- 优化:业务服务启动不用依赖网关
|
||||
- 2025-03-12:优化dubbo filter
|
||||
- 2025-03-09:优先使用本地缓存
|
||||
- 2025-03-06:RouteContext新增isv对象
|
||||
- 2025-03-05:变更拦截器方法参数
|
||||
- 2025-03-04:拦截器新增init方法,用来做一些初始化工作
|
||||
- 2025-02-27:新增token校验,com.gitee.sop.gateway.interceptor.internal.TokenValidateInterceptor.checkToken
|
||||
- 2025-02-20:修复Linux环境下启动报错,加载i18n问题;优化新增接口注册保存逻辑
|
||||
- 2025-02-19:升级fastmybatis到3.0.16
|
||||
- 2025-02-09:优化Restful接口校验
|
||||
- 2025-02-07:优化菜单排序
|
||||
- 2025-02-04:新增Restful模式
|
||||
|
||||
## 1.15.1
|
||||
## 5.1
|
||||
|
||||
- 修复未配置正确MessageConverter导致的异常
|
||||
接入smart-doc
|
||||
|
||||
## 1.15.0
|
||||
## 5.0
|
||||
|
||||
- 优化预发布、灰度
|
||||
- 网关动态修改请求参数
|
||||
- 支持swagger-bootstrap插件
|
||||
- 优化admin服务列表显示
|
||||
- 优化文档刷新逻辑
|
||||
- 新增测试all in one
|
||||
- 修复中文乱码问题
|
||||
|
||||
## 1.14.0
|
||||
|
||||
- 支持预发布、灰度发布环境
|
||||
|
||||
## 1.13.7
|
||||
|
||||
- 修复修复context-path识别问题
|
||||
|
||||
## 1.13.6
|
||||
|
||||
- 修复@RequestBody不能绑定问题
|
||||
|
||||
## 1.13.5
|
||||
|
||||
- 修复postJson下version获取不到问题
|
||||
|
||||
## 1.13.4
|
||||
|
||||
- 修复admin服务列表最后更新时间不显示问题
|
||||
- 优化上传路由配置逻辑
|
||||
- 微服务可获得access_token, notify_url参数
|
||||
|
||||
## 1.13.3
|
||||
|
||||
- 优化参数绑定
|
||||
|
||||
## 1.13.2
|
||||
|
||||
- 修复json方式请求获取不到参数问题
|
||||
- 微服务端新增获取开放平台请求参数
|
||||
|
||||
## 1.13.1
|
||||
|
||||
- 支持json方式请求(application/json)
|
||||
- 支持传统web服务开发(见文档`传统web开发`)
|
||||
|
||||
## 1.13.0
|
||||
|
||||
- 新增IP黑名单
|
||||
|
||||
## 1.12.4
|
||||
|
||||
- 优化属性文件配置
|
||||
- 新增sleuth接入文档
|
||||
- admin的isv列表新增备注字段
|
||||
|
||||
## 1.12.3
|
||||
|
||||
- 修复删除zk节点导致的BUG
|
||||
|
||||
## 1.12.2
|
||||
|
||||
- 沙盒支持文件上传
|
||||
|
||||
## 1.12.1
|
||||
|
||||
- 修复重启网关路由状态重置BUG
|
||||
- 优化SpringCloudGateway
|
||||
|
||||
## 1.12.0
|
||||
|
||||
- admin后台新增角色管理
|
||||
- 支持nacos作为注册中心
|
||||
|
||||
## 1.11.0
|
||||
|
||||
- 秘钥管理改造
|
||||
- 服务端返回sign
|
||||
- 新增SDK返回sign处理
|
||||
- 新增沙箱环境
|
||||
|
||||
## 1.10.0
|
||||
|
||||
- 新增监控日志
|
||||
|
||||
## 1.9.0
|
||||
|
||||
- 改造限流
|
||||
- 增强参数绑定
|
||||
|
||||
## 1.8.0
|
||||
|
||||
- 支持文件上传
|
||||
|
||||
## 1.7.2
|
||||
|
||||
- 修复微服务参数绑定BUG
|
||||
- Admin新增vue界面
|
||||
|
||||
## 1.7.1
|
||||
|
||||
- 支持接口名版本号放在url后面
|
||||
|
||||
## 1.7.0
|
||||
|
||||
- 可自定义数据节点名称
|
||||
|
||||
## 1.6.0
|
||||
|
||||
- 新增应用授权
|
||||
|
||||
## 1.5.0
|
||||
|
||||
- admin新增signType字段
|
||||
- 修复easyopen接入无法访问BUG
|
||||
|
||||
## 1.4.0
|
||||
|
||||
- 新增文档分组显示
|
||||
- 支持easyopen文档注解
|
||||
- BUG修复
|
||||
|
||||
## 1.3.0
|
||||
|
||||
- 新增接口限流功能 [doc](http://durcframework.gitee.io/sop/#/files/10092_%E6%8E%A5%E5%8F%A3%E9%99%90%E6%B5%81?t=1555378655699)
|
||||
- 新增文档整合功能 [doc](http://durcframework.gitee.io/sop/#/files/10041_%E7%BC%96%E5%86%99%E6%96%87%E6%A1%A3?t=1555378655698)
|
||||
- 新增springmvc项目接入demo
|
||||
|
||||
## 1.2.0
|
||||
|
||||
- SOP Admin新增用户登录
|
||||
- 新增基础SDK(Java,C#) [doc](http://durcframework.gitee.io/sop/#/files/10095_SDK%E5%BC%80%E5%8F%91?t=1554693919597)
|
||||
|
||||
## 1.1.0
|
||||
|
||||
- 新增ISV管理 [doc](http://durcframework.gitee.io/sop/#/files/10085_ISV%E7%AE%A1%E7%90%86?t=1554123435621)
|
||||
- 新增接口授权 [doc](http://durcframework.gitee.io/sop/#/files/10090_%E8%B7%AF%E7%94%B1%E6%8E%88%E6%9D%83?t=1554123435621)
|
||||
|
||||
## 1.0.0
|
||||
|
||||
- 第一次发布
|
||||
全面重构,欢迎体验:[文档](https://xcnm3jsc8anf.feishu.cn/wiki/IJygwHV5Wij9i9kZ2yEcTblbnPg)
|
||||
|
||||
176
checkstyle.xml
Executable file
@@ -0,0 +1,176 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
|
||||
<module name="Checker">
|
||||
<!-- 文件长度不超过1500行 -->
|
||||
<module name="FileLength">
|
||||
<property name="max" value="2500"/>
|
||||
</module>
|
||||
|
||||
<!-- 长度检查 -->
|
||||
<!-- 每行不超过200个字符 -->
|
||||
<module name="LineLength">
|
||||
<property name="max" value="400"/>
|
||||
</module>
|
||||
<module name="SuppressWarningsFilter" />
|
||||
<!-- 每个java文件一个语法树 -->
|
||||
<module name="TreeWalker">
|
||||
<module name="SuppressWarningsHolder" />
|
||||
<!-- import检查-->
|
||||
<!-- 检查是否从非法的包中导入了类 -->
|
||||
<module name="IllegalImport"/>
|
||||
<!-- 检查是否导入了多余的包 -->
|
||||
<module name="RedundantImport"/>
|
||||
<!-- 没用的import检查,比如:1.没有被用到2.重复的3.import java.lang的4.import 与该类在同一个package的 -->
|
||||
<module name="UnusedImports"/>
|
||||
|
||||
<!-- 注释检查 -->
|
||||
<!-- 检查构造函数的javadoc -->
|
||||
<module name="JavadocType">
|
||||
<property name="allowUnknownTags" value="true"/>
|
||||
<message key="javadoc.missing" value="类注释:缺少Javadoc注释。"/>
|
||||
</module>
|
||||
|
||||
<!-- 命名检查 -->
|
||||
<!-- 局部的final变量,包括catch中的参数的检查 -->
|
||||
<module name="LocalFinalVariableName"/>
|
||||
<!-- 局部的非final型的变量,包括catch中的参数的检查 -->
|
||||
<module name="LocalVariableName"/>
|
||||
<!-- 包名的检查(只允许小写字母),默认^[a-z]+(\.[a-zA-Z_][a-zA-Z_0-9_]*)*$ -->
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
<message key="name.invalidPattern" value="包名 ''{0}'' 要符合 ''{1}''格式."/>
|
||||
</module>
|
||||
<!-- 仅仅是static型的变量(不包括static final型)的检查 -->
|
||||
<module name="StaticVariableName"/>
|
||||
<!-- Class或Interface名检查,默认^[A-Z][a-zA-Z0-9]*$-->
|
||||
<module name="TypeName">
|
||||
<property name="severity" value="warning"/>
|
||||
<message key="name.invalidPattern" value="名称 ''{0}'' 要符合 ''{1}''格式."/>
|
||||
</module>
|
||||
<!-- 非static型变量的检查
|
||||
<module name="MemberName"/>
|
||||
-->
|
||||
<!-- 方法名的检查 -->
|
||||
<module name="MethodName"/>
|
||||
<!-- 方法的参数名
|
||||
<module name="ParameterName "/>
|
||||
-->
|
||||
<!-- 常量名的检查(只允许大写),默认^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$ -->
|
||||
<module name="ConstantName"/>
|
||||
|
||||
<!-- 定义检查 -->
|
||||
<!-- 检查数组类型定义的样式 -->
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<!-- 检查long型定义是否有大写的“L” -->
|
||||
<module name="UpperEll"/>
|
||||
<!-- 方法不超过100行 -->
|
||||
<module name="MethodLength">
|
||||
<property name="tokens" value="METHOD_DEF"/>
|
||||
<property name="max" value="300"/>
|
||||
</module>
|
||||
<!-- 方法的参数个数不超过8个。 并且不对构造方法进行检查-->
|
||||
<module name="ParameterNumber">
|
||||
<property name="max" value="8"/>
|
||||
<property name="ignoreOverriddenMethods" value="true"/>
|
||||
<property name="tokens" value="METHOD_DEF"/>
|
||||
</module>
|
||||
|
||||
<!-- 空格检查-->
|
||||
<!-- 方法名后跟左圆括号"(" -->
|
||||
<module name="MethodParamPad"/>
|
||||
<!-- 在类型转换时,不允许左圆括号右边有空格,也不允许与右圆括号左边有空格 -->
|
||||
<module name="TypecastParenPad"/>
|
||||
<!-- 检查在某个特定关键字之后应保留空格 -->
|
||||
<module name="NoWhitespaceAfter"/>
|
||||
<!-- 检查在某个特定关键字之前应保留空格 -->
|
||||
<module name="NoWhitespaceBefore"/>
|
||||
<!-- 圆括号空白 -->
|
||||
<module name="ParenPad"/>
|
||||
<!-- 检查分隔符是否在空白之后 -->
|
||||
<module name="WhitespaceAfter"/>
|
||||
<!-- 检查分隔符周围是否有空白 -->
|
||||
<module name="WhitespaceAround"/>
|
||||
|
||||
<!-- 修饰符检查 -->
|
||||
<!-- 检查修饰符的顺序是否遵照java语言规范,默认public、protected、private、abstract、static、final、transient、volatile、synchronized、native、strictfp -->
|
||||
<module name="ModifierOrder"/>
|
||||
<!-- 检查接口和annotation中是否有多余修饰符,如接口方法不必使用public -->
|
||||
<module name="RedundantModifier"/>
|
||||
|
||||
<!-- 代码块检查 -->
|
||||
<!-- 检查是否有嵌套代码块 -->
|
||||
<module name="AvoidNestedBlocks"/>
|
||||
<!-- 检查是否有空代码块 -->
|
||||
<module name="EmptyBlock"/>
|
||||
<!-- 检查左大括号位置 -->
|
||||
<module name="LeftCurly"/>
|
||||
<!-- 检查代码块是否缺失{} -->
|
||||
<module name="NeedBraces"/>
|
||||
<!-- 检查右大括号位置 -->
|
||||
<module name="RightCurly"/>
|
||||
|
||||
<!-- 代码检查 -->
|
||||
<!-- 检查空的代码段 -->
|
||||
<module name="EmptyStatement"/>
|
||||
<!-- 检查在重写了equals方法后是否重写了hashCode方法 -->
|
||||
<module name="EqualsHashCode"/>
|
||||
<!-- 检查局部变量或参数是否隐藏了类中的变量 -->
|
||||
<module name="HiddenField">
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
</module>
|
||||
<!-- 检查子表达式中是否有赋值操作 -->
|
||||
<module name="InnerAssignment"/>
|
||||
<!-- 检查switch语句是否有default -->
|
||||
<module name="MissingSwitchDefault"/>
|
||||
<!-- 检查是否有过度复杂的布尔表达式 -->
|
||||
<module name="SimplifyBooleanExpression"/>
|
||||
<!-- 检查是否有过于复杂的布尔返回代码段 -->
|
||||
<module name="SimplifyBooleanReturn"/>
|
||||
|
||||
<!-- 类设计检查 -->
|
||||
<!-- 检查类是否为扩展设计l -->
|
||||
<!-- 检查只有private构造函数的类是否声明为final
|
||||
<module name="FinalClass"/>
|
||||
-->
|
||||
<!-- 检查接口是否仅定义类型 -->
|
||||
<module name="InterfaceIsType"/>
|
||||
<!-- 检查类成员的可见度 检查类成员的可见性。只有static final 成员是public的
|
||||
除非在本检查的protectedAllowed和packagedAllowed属性中进行了设置-->
|
||||
<module name="VisibilityModifier">
|
||||
<property name="packageAllowed" value="true"/>
|
||||
<property name="protectedAllowed" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- 语法 -->
|
||||
<!-- String的比较不能用!= 和 == -->
|
||||
<module name="StringLiteralEquality"/>
|
||||
<!-- 限制for循环最多嵌套2层 -->
|
||||
<module name="NestedForDepth">
|
||||
<property name="max" value="2"/>
|
||||
</module>
|
||||
<!-- if最多嵌套3层 -->
|
||||
<module name="NestedIfDepth">
|
||||
<property name="max" value="10"/>
|
||||
</module>
|
||||
<!-- 检查未被注释的main方法,排除以Appllication结尾命名的类 -->
|
||||
<module name="UncommentedMain">
|
||||
<property name="excludedClasses" value=".*[Application,Test]$"/>
|
||||
</module>
|
||||
<!-- 禁止使用System.out.println -->
|
||||
<module name="Regexp">
|
||||
<property name="format" value="System\.out\.println"/>
|
||||
<property name="illegalPattern" value="true"/>
|
||||
</module>
|
||||
<!--try catch 异常处理数量 3-->
|
||||
<module name="NestedTryDepth ">
|
||||
<property name="max" value="3"/>
|
||||
</module>
|
||||
<!-- clone方法必须调用了super.clone() -->
|
||||
<module name="SuperClone"/>
|
||||
<!-- finalize 必须调用了super.finalize() -->
|
||||
<module name="SuperFinalize"/>
|
||||
</module>
|
||||
</module>
|
||||
@@ -1,11 +0,0 @@
|
||||
# 开发文档
|
||||
|
||||
文档放在docs/files下,写完文档记得执行下`SidebarTest.main()`方法
|
||||
|
||||
配合gitee pages服务使用,gitee pages服务指定docs目录。
|
||||
|
||||
## 本地查看开发文档
|
||||
|
||||
- 前提:先安装好npm,[npm安装教程](https://blog.csdn.net/zhangwenwu2/article/details/52778521)。建议使用淘宝镜像。
|
||||
- 安装docsify,执行npm命令`npm i docsify-cli -g --registry=https://registry.npm.taobao.org`
|
||||
- cd到当前目录,运行命令`docsify serve docs`,然后访问:`http://localhost:3000`即可查看。
|
||||
@@ -1,5 +0,0 @@
|
||||
# SOP开发文档
|
||||
|
||||
Git地址:[SOP](https://gitee.com/durcframework/SOP)
|
||||
|
||||
Q群:328419269
|
||||
@@ -1 +0,0 @@
|
||||
include: [_navbar,_sidebar]
|
||||
@@ -1,12 +0,0 @@
|
||||

|
||||
|
||||
# docsify <small>4.6.10</small>
|
||||
|
||||
> A magical documentation site generator.
|
||||
|
||||
* Simple and lightweight (~19kB gzipped)
|
||||
* No statically built html files
|
||||
* Multiple themes
|
||||
|
||||
[GitHub](https://github.com/QingWei-Li/docsify/)
|
||||
[Get Started](#docsify)
|
||||
@@ -1,3 +0,0 @@
|
||||
- 关于
|
||||
- [帮助](/zh-cn/)
|
||||
- [API](/)
|
||||
@@ -1,34 +0,0 @@
|
||||
* [首页](/?t=1565916799573)
|
||||
* 开发文档
|
||||
* [快速体验](files/10010_快速体验.md?t=1565916799577)
|
||||
* [项目接入到SOP](files/10011_项目接入到SOP.md?t=1565916799596)
|
||||
* [新增接口](files/10020_新增接口.md?t=1565916799596)
|
||||
* [业务参数校验](files/10030_业务参数校验.md?t=1565916799596)
|
||||
* [错误处理](files/10040_错误处理.md?t=1565916799596)
|
||||
* [编写文档](files/10041_编写文档.md?t=1565916799596)
|
||||
* [接口交互详解](files/10050_接口交互详解.md?t=1565916799597)
|
||||
* [easyopen支持](files/10070_easyopen支持.md?t=1565916799597)
|
||||
* [使用签名校验工具](files/10080_使用签名校验工具.md?t=1565916799597)
|
||||
* [ISV管理](files/10085_ISV管理.md?t=1565916799597)
|
||||
* [自定义路由](files/10086_自定义路由.md?t=1565916799597)
|
||||
* [自定义返回结果](files/10087_自定义返回结果.md?t=1565916799597)
|
||||
* [自定义过滤器](files/10088_自定义过滤器.md?t=1565916799597)
|
||||
* [路由授权](files/10090_路由授权.md?t=1565916799598)
|
||||
* [接口限流](files/10092_接口限流.md?t=1565916799598)
|
||||
* [监控日志](files/10093_监控日志.md?t=1565916799598)
|
||||
* [SDK开发](files/10095_SDK开发.md?t=1565916799598)
|
||||
* [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1565916799598)
|
||||
* [应用授权](files/10097_应用授权.md?t=1565916799598)
|
||||
* [传统web开发](files/10100_传统web开发.md?t=1565916799598)
|
||||
* [文件上传](files/10104_文件上传.md?t=1565916799598)
|
||||
* [nacos注册中心](files/10106_nacos注册中心.md?t=1565916799599)
|
||||
* [扩展其它注册中心](files/10107_扩展其它注册中心.md?t=1565916799599)
|
||||
* [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1565916799599)
|
||||
* [预发布灰度发布](files/10110_预发布灰度发布.md?t=1565916799599)
|
||||
* [动态修改请求参数](files/10111_动态修改请求参数.md?t=1565916799599)
|
||||
* 原理分析
|
||||
* [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1565916799599)
|
||||
* [原理分析之路由存储](files/90011_原理分析之路由存储.md?t=1565916799599)
|
||||
* [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1565916799599)
|
||||
* [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1565916799600)
|
||||
* [常见问题](files/90100_常见问题.md?t=1565916799600)
|
||||
@@ -1,28 +0,0 @@
|
||||
# 快速体验
|
||||
|
||||
> 运行环境:JDK8,Maven3,Zookeeper,Mysql
|
||||
|
||||
- 安装并启动zookeeper,[安装教程](http://zookeeper.apache.org/doc/r3.4.13/zookeeperStarted.html)
|
||||
- 执行Mysql脚本`sop.sql`
|
||||
- IDE安装lombok插件,然后打开项目(IDEA下可以打开根pom.xml,然后open as project)
|
||||
- 启动注册中心,sop-registry(运行SopRegistryApplication.java)
|
||||
- 启动网关:打开sop-gateway下的`application-dev.properties`
|
||||
1. 修改数据库`username/password`
|
||||
2. 指定zookeeper地址,如果zookeeper安装在本机则不用改
|
||||
3. 运行`SopGatewayApplication.java`
|
||||
- 启动微服务:打开sop-story-web下的`application-dev.properties`文件
|
||||
1. 指定zookeeper地址,如果zookeeper安装在本机则不用改
|
||||
2. 运行`SopStoryApplication.java`
|
||||
- 找到sop-test,打开`AllInOneTest.java`进行接口调用测试
|
||||
|
||||
确保注册中心先启动
|
||||
|
||||
## 使用admin
|
||||
|
||||
- 找到`sop-admin/sop-admin-server`工程,打开sop-admin-server下的`application-dev.properties`,修改相关配置
|
||||
- 运行`SopAdminServerApplication.java`
|
||||
- 访问:`http://localhost:8082`
|
||||
|
||||
登录账号:admin/123456
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
# 项目接入到SOP
|
||||
|
||||
以springboot项目为例,springmvc目前暂不支持,以后可以支持。
|
||||
|
||||
- pom.xml添加SpringCloud支持
|
||||
|
||||
```xml
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Greenwich.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
```
|
||||
|
||||
- pom.xml依赖sop-service-common和eureka
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-service-common</artifactId>
|
||||
<version>最新版本</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- application.properties配置文件添加
|
||||
|
||||
```properties
|
||||
server.port=2222
|
||||
# 服务名称
|
||||
spring.application.name=story-service
|
||||
# eureka注册中心
|
||||
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
|
||||
# zookeeper配置
|
||||
spring.cloud.zookeeper.connect-string=localhost:2181
|
||||
```
|
||||
|
||||
- 在springboot启动类上添加`@EnableDiscoveryClient`
|
||||
- 新增一个配置类,继承`AlipayServiceConfiguration.java`,内容为空
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class OpenServiceConfig extends AlipayServiceConfiguration {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
到此准备工作就完成了,接下来可前往`新增接口`查看如何新增接口。
|
||||
@@ -1,212 +0,0 @@
|
||||
# 新增接口
|
||||
|
||||
以story服务为例,新增一个获取故事内容接口
|
||||
|
||||
- 在controller下新建一个类,StoryDemoController.java
|
||||
- 加上`@RestController`注解
|
||||
|
||||
```java
|
||||
@RestController
|
||||
public class StoryDemoController {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
- 新增一个接口
|
||||
|
||||
```java
|
||||
@ApiMapping(value = "story.demo.get")
|
||||
public Story getStory() {
|
||||
Story story = new Story();
|
||||
story.setId(1);
|
||||
story.setName("白雪公主");
|
||||
return story;
|
||||
}
|
||||
```
|
||||
|
||||
这里的`@ApiMapping`注解作用同`@RequestMapping`注解,可以理解为是它的扩展
|
||||
|
||||
value就是接口名,对应客户端的`method`参数
|
||||
|
||||
如果要加上版本号,指定`version`参数:`@ApiMapping(value = "story.demo.get", version = "2.0")`
|
||||
|
||||
- 重启story服务,这样接口就可以使用了。
|
||||
|
||||
## 绑定业务参数
|
||||
|
||||
网关校验通过后,请求参数会传递到微服务上来,完整的参数如下所示:
|
||||
|
||||
```
|
||||
请求参数:charset=utf-8&biz_content={"goods_remark":"iphone6"}&method=goods.add&format=json&app_id=2019032617262200001&sign_type=RSA2&version=1.0×tamp=2019-04-29 19:18:38
|
||||
```
|
||||
|
||||
其中biz_content部分是我们想要的,在方法上申明一个对象,对应biz_content中的内容即可完成参数绑定,并且对参数进行JSR-303校验。
|
||||
|
||||
```java
|
||||
@ApiMapping(value = "goods.add")
|
||||
public Object addGoods(GoodsParam param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@Data
|
||||
public class GoodsParam {
|
||||
@NotEmpty(message = "不能为空") // 支持JSR-303校验
|
||||
private String goods_remark;
|
||||
}
|
||||
```
|
||||
|
||||
一般情况下,只需要获取业务参数即可,如果想要获取更多的参数,可在后面跟一个`HttpServletRequest`对象。
|
||||
|
||||
```java
|
||||
@ApiMapping(value = "goods.add")
|
||||
public Object addGoods(GoodsParam param, HttpServletRequest request) {
|
||||
System.out.println(request.getParameter("method"));
|
||||
return param;
|
||||
}
|
||||
```
|
||||
|
||||
- 方式2
|
||||
|
||||
```java
|
||||
@ApiMapping(value = "story.get", version = "2.2")
|
||||
public Story getStory22(OpenContext<Story> openContext) {
|
||||
// 业务参数
|
||||
Story bizObject = openContext.getBizObject();
|
||||
// 获取appid,更多方法查看OpenContext类
|
||||
String appId = openContext.getAppId();
|
||||
System.out.println(appId);
|
||||
return bizObject;
|
||||
}
|
||||
```
|
||||
|
||||
另一种方式,OpenContext泛型参数填bizObject类,调用openContext.getBizObject()可直接获得对象
|
||||
|
||||
此方式等价于:
|
||||
|
||||
```java
|
||||
@ApiMapping(value = "story.get", version = "2.2")
|
||||
public Story getStory22(Story bizObject) {
|
||||
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
|
||||
String appId = openContext.getAppId();
|
||||
System.out.println(appId);
|
||||
return bizObject;
|
||||
}
|
||||
```
|
||||
|
||||
## 接口命名
|
||||
|
||||
接口命名没有做强制要求,但我们还是推荐按照下面的方式进行命名:
|
||||
|
||||
接口名的命名规则为:`服务模块.业务模块.功能模块.行为`,如:
|
||||
|
||||
- mini.user.userinfo.get 小程序服务.用户模块.用户信息.获取
|
||||
- member.register.total.get 会员服务.注册模块.注册总数.获取
|
||||
|
||||
如果觉得命名规则有点长可以精简为:`服务模块.功能模块.行为`,如`member.usercount.get`,前提是确保前缀要有所区分,不和其它服务冲突。
|
||||
|
||||
## 测试接口
|
||||
|
||||
- 在sop-test工程下新建一个测试用例,`StoryDemoTest`,继承TestBase
|
||||
|
||||
```java
|
||||
public class StoryDemoTest extends TestBase {
|
||||
|
||||
String url = "http://localhost:8081/api"; // zuul
|
||||
String appId = "2019032617262200001";
|
||||
// 私钥
|
||||
String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=";
|
||||
|
||||
|
||||
@Test
|
||||
public void testDemo() throws Exception {
|
||||
// 公共请求参数
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("app_id", appId);
|
||||
// 这里对应@ApiMapping.value属性
|
||||
params.put("method", "story.demo.get");
|
||||
params.put("format", "json");
|
||||
params.put("charset", "utf-8");
|
||||
params.put("sign_type", "RSA2");
|
||||
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
|
||||
// 这里对应@ApiMapping.version属性
|
||||
params.put("version", "1.0");
|
||||
|
||||
// 业务参数
|
||||
Map<String, String> bizContent = new HashMap<>();
|
||||
|
||||
params.put("biz_content", JSON.toJSONString(bizContent));
|
||||
|
||||
System.out.println("----------- 请求信息 -----------");
|
||||
System.out.println("请求参数:" + buildParamQuery(params));
|
||||
System.out.println("商户秘钥:" + privateKey);
|
||||
String content = AlipaySignature.getSignContent(params);
|
||||
System.out.println("待签名内容:" + content);
|
||||
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
|
||||
System.out.println("签名(sign):" + sign);
|
||||
|
||||
params.put("sign", sign);
|
||||
|
||||
System.out.println("----------- 返回结果 -----------");
|
||||
String responseData = post(url, params);// 发送请求
|
||||
System.out.println(responseData);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
- 请求成功后,控制台会打印:
|
||||
|
||||
```
|
||||
----------- 请求信息 -----------
|
||||
请求参数:charset=utf-8&biz_content={}&method=story.demo.get&format=json&app_id=alipay_test&sign_type=RSA2&version=1.0×tamp=2019-03-23 15:41:22
|
||||
商户秘钥:MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=
|
||||
待签名内容:app_id=alipay_test&biz_content={}&charset=utf-8&format=json&method=story.demo.get&sign_type=RSA2×tamp=2019-03-23 15:41:22&version=1.0
|
||||
签名(sign):YMbxTPdovi6htcn1K3USTS6/Tbg6MOAMigG6x/kG0kQFCYH8ljvxXzcY86UT056nUG3OXxnj0xkw07eV6E03HMlu7bn3/jrT3PCcV3YguhA92aWz720x2xJWdfXY13OUPS9VOCC9zIVxu6EBD+PoZ7ojYChYvOfCR5I8bR/oOc0ZLjK63PWTBdf0eFS4sybXzRf81uNLMROsMhmBDDy0Fhml3ml77qzWBIpsmq5ECZ+89rMPbkNhAUcnFAe7ik7xZIL6WcUhAOhKVa8ZQK1GMjoGnAbGRed1FbuOHZGubgffg4/vMqrY10Bcy6h9jt/zK5w9L3HVgK3aPgQlfP16Gg==
|
||||
----------- 返回结果 -----------
|
||||
{"story_demo_get_response":{"msg":"Success","code":"10000","name":"白雪公主","id":1},"sign":"YMbxTPdovi6htcn1K3USTS6/Tbg6MOAMigG6x/kG0kQFCYH8ljvxXzcY86UT056nUG3OXxnj0xkw07eV6E03HMlu7bn3/jrT3PCcV3YguhA92aWz720x2xJWdfXY13OUPS9VOCC9zIVxu6EBD+PoZ7ojYChYvOfCR5I8bR/oOc0ZLjK63PWTBdf0eFS4sybXzRf81uNLMROsMhmBDDy0Fhml3ml77qzWBIpsmq5ECZ+89rMPbkNhAUcnFAe7ik7xZIL6WcUhAOhKVa8ZQK1GMjoGnAbGRed1FbuOHZGubgffg4/vMqrY10Bcy6h9jt/zK5w9L3HVgK3aPgQlfP16Gg=="}
|
||||
```
|
||||
|
||||
## 开放现有接口
|
||||
|
||||
如果想把现有项目中的接口开放出去,提供给客户调用,具体操作如下:
|
||||
|
||||
- 将现有项目接入到SOP,前往`项目接入到SOP`文档页查看
|
||||
- 在现有接口方法上加上一个注解`@ApiAbility`,如下面这个接口
|
||||
|
||||
```java
|
||||
// 具备开放平台能力
|
||||
@ApiAbility
|
||||
@RequestMapping("getStory2")
|
||||
public Story getStory2_0() {
|
||||
Story story = new Story();
|
||||
story.setId(1);
|
||||
story.setName("海底小纵队(默认版本号)");
|
||||
return story;
|
||||
}
|
||||
```
|
||||
|
||||
- 启动程序
|
||||
|
||||
这种情况下,老接口依然能正常访问,同时开放平台也能访问进来。
|
||||
|
||||
**注意** 此时的开放接口对应的接口名为:类@RequestMapping.value + "." + 方法@RequestMapping.value
|
||||
|
||||
举个列子:
|
||||
|
||||
```java
|
||||
@RequestMapping("goods")
|
||||
public class MyController {
|
||||
@ApiAbility
|
||||
@RequestMapping("listGoods")
|
||||
public Object fun() {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
fun接口对应的路径为:`/goods/listGoods`
|
||||
|
||||
那么对应开放平台的接口名会转换成:`goods.listGoods`,客户端的method参数要填`goods.listGoods`
|
||||
|
||||
当然也可以直接把@RequestMapping替换成`@ApiMapping`并指定接口名,这样的话不能兼容以前的访问形式。
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
# 业务参数校验
|
||||
|
||||
业务参数校验采用JSR-303方式,关于JSR-303介绍可以参考这篇博文:[JSR 303 - Bean Validation 介绍及最佳实践](https://www.ibm.com/developerworks/cn/java/j-lo-jsr303/)
|
||||
|
||||
在参数中使用注解即可,框架会自动进行验证。如下面一个添加商品接口,它的参数是GoodsParam
|
||||
|
||||
```java
|
||||
@ApiMapping(value = "goods.add")
|
||||
public void addGoods(GoodsParam param) {
|
||||
...
|
||||
}
|
||||
```
|
||||
在GoodsParam中添加JSR-303注解:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class GoodsParam {
|
||||
@NotEmpty(message = "商品名称不能为空")
|
||||
private String goods_name;
|
||||
}
|
||||
```
|
||||
|
||||
如果不传商品名称则返回
|
||||
|
||||
```
|
||||
{"goods_add_response":{"msg":"Success","code":"10000","sub_msg":"商品名称不能为空","sub_code":"isv.invalid-parameter"},"sign":"Eh3Z5CxDCHsb4MyYFVxsPSmBpwVi1LISJdOkrzglxXoqG7RVyEOt4ef1kNpznUvMI3FDQU1suR7Rsmx6NjGdEVS6NSH2Kt0d8TFBRpLhWz8hApnxOtgzqMqbYeMuJie7X5gF6m8hTnvuuxF21IrkixMe+lyBcXw7dk0C3w1SwdEZkHQ+xC+M4bLqAZt5/3kl79/FWSMFJWHiZmg5YeEi8e8XhYCNcz+xlJRJL0x2Y87fFxqSY0UYWNxbQHgdVI8xRfn1n31nzkcLxiAtTh4LPtNRrG7w7absK/C1Oi/vczuBlFeq2EWUsYVWOVpKiJifUwvYVUUsztSLElzplzOjbg=="}
|
||||
|
||||
```
|
||||
|
||||
## 参数校验国际化
|
||||
|
||||
国际化的配置方式如下:
|
||||
|
||||
```java
|
||||
@NotEmpty(message = "{goods.remark.notNull}")
|
||||
private String goods_remark;
|
||||
```
|
||||
|
||||
国际化资源文件`bizerror_en.properties`中添加:
|
||||
```
|
||||
goods.remark.notNull=The goods_remark can not be null
|
||||
```
|
||||
|
||||
bizerror_zh_CN.properties中添加:
|
||||
|
||||
```
|
||||
# 商品备注不能为空
|
||||
goods.remark.notNull=\u5546\u54c1\u5907\u6ce8\u4e0d\u80fd\u4e3a\u7a7a
|
||||
```
|
||||
|
||||
## 参数校验国际化传参
|
||||
|
||||
下面校验商品评论的长度,要求大于等于3且小于等于20。数字3和20要填充到国际化资源中去。
|
||||
|
||||
```
|
||||
// 传参的格式:{xxx}=value1,value2...
|
||||
@Length(min = 3, max = 20, message = "{goods.comment.length}=3,20")
|
||||
private String goods_comment;
|
||||
```
|
||||
|
||||
bizerror_en.properties:
|
||||
```
|
||||
goods.comment.length=The goods_comment length must >= {0} and <= {1}
|
||||
```
|
||||
|
||||
bizerror_zh_CN.properties中添加:
|
||||
|
||||
```
|
||||
# 商品评论长度必须在{0}和{1}之间
|
||||
goods.comment.length=\u5546\u54c1\u8bc4\u8bba\u957f\u5ea6\u5fc5\u987b\u5728{0}\u548c{1}\u4e4b\u95f4
|
||||
```
|
||||
这样value1,value2会分别填充到{0},{1}中
|
||||
@@ -1,91 +0,0 @@
|
||||
# 错误处理
|
||||
|
||||
SOP对错误处理已经封装好了,简单做法是`throw ServiceException`,在最顶层的Controller会做统一处理。例如:
|
||||
|
||||
```java
|
||||
if(StringUtils.isEmpty(param.getGoods_name())) {
|
||||
throw new ServiceException("goods_name不能为null");
|
||||
}
|
||||
```
|
||||
|
||||
为了保证编码风格的一致性,推荐统一使用ServiceException
|
||||
|
||||
## i18n国际化
|
||||
|
||||
SOP支持国际化消息。通过Request对象中的getLocale()来决定具体返回那种语言,客户端通过设置Accept-Language头部来决定返回哪种语言,中文是zh,英文是en。
|
||||
|
||||
SOP通过模块化来管理国际化消息,这样做的好处结构清晰,维护方便。下面就来讲解如何设置国际化消息。
|
||||
|
||||
以story服务为例,假设我们要对商品模块进行设置,步骤如下:
|
||||
|
||||
- 在`resource/i18n/isp`目录下新建goods_error_zh_CN.properties属性文件
|
||||
|
||||
属性文件的文件名有规律, **i18n/isp/goods_error** 表示模块路径, **_zh_CN.properties** 表示中文错误消息。如果要使用英文错误,则新建一个`goods_error_en.properties`即可。
|
||||
|
||||
- 在goods_error_zh_CN.properties中配置错误信息
|
||||
|
||||
```
|
||||
# 商品名字不能为空
|
||||
isp.goods_error_100=\u5546\u54C1\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
|
||||
```
|
||||
|
||||
isp.goods_error_为固定前缀,100为错误码,这两个值后续会用到。
|
||||
|
||||
接下来是把属性文件加载到国际化容器当中。
|
||||
|
||||
- 添加国际化配置,在OpenServiceConfig中的static块中添加,代码如下:
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class OpenServiceConfig extends AlipayServiceConfiguration {
|
||||
|
||||
static {
|
||||
ServiceConfig.getInstance().getI18nModules().add("i18n/isp/goods_error");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 新建一个枚举用来定义错误
|
||||
|
||||
```java
|
||||
// 按模块来定义异常消息,团队开发可以分开进行
|
||||
public enum GoodsErrorEnum {
|
||||
/** 参数错误 */
|
||||
NO_GOODS_NAME("100"),
|
||||
;
|
||||
private ServiceErrorMeta errorMeta;
|
||||
|
||||
StoryErrorEnum(String subCode) {
|
||||
this.errorMeta = new ServiceErrorMeta("isp.goods_error_", subCode);
|
||||
}
|
||||
|
||||
public ServiceErrorMeta getErrorMeta() {
|
||||
return errorMeta;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
接下来就可以使用了
|
||||
|
||||
```java
|
||||
if (StringUtils.isEmpty(param.getGoods_name())) {
|
||||
throw GoodsErrorEnum.NO_GOODS_NAME.getErrorMeta().getException();
|
||||
}
|
||||
```
|
||||
|
||||
### 国际化消息传参
|
||||
|
||||
即代码中变量传入到properties文件中去,做法是采用{0},{1}占位符。0代表第一个参数,1表示第二个参数。
|
||||
|
||||
```
|
||||
# 商品名称太短,不能小于{0}个字
|
||||
isp.goods_error_101=\u5546\u54C1\u540D\u79F0\u592A\u77ED\uFF0C\u4E0D\u80FD\u5C0F\u4E8E{0}\u4E2A\u5B57
|
||||
```
|
||||
|
||||
```java
|
||||
if (param.getGoods_name().length() <= 3) {
|
||||
throw GoodsErrorEnum.LESS_GOODS_NAME_LEN.getErrorMeta().getException(3);
|
||||
}
|
||||
```
|
||||
直接放进getException(Object... params)方法参数中,因为是可变参数,可随意放。
|
||||
@@ -1,119 +0,0 @@
|
||||
# 编写文档
|
||||
|
||||
作为开放平台,必须要提供API文档。
|
||||
|
||||
SOP采用微服务架构实现,因此文档应该由各个微服务各自实现。难点就是如何统一归纳各个微服务端提供的文档信息,并且统一展示。
|
||||
|
||||
写完接口后使用swagger注解来定义自己的文档信息。步骤如下:
|
||||
|
||||
- maven添加swagger
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>2.9.2</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
<version>1.5.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>2.9.2</version>
|
||||
</dependency>
|
||||
|
||||
```
|
||||
|
||||
- 在config中添加swagger配置
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class OpenServiceConfig extends AlipayServiceConfiguration {
|
||||
// 开启文档
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public static class Swagger2 extends SwaggerSupport {
|
||||
@Override
|
||||
protected String getDocTitle() {
|
||||
// 不能重复。比如订单服务返回:`订单API`;库存服务返回:`库存API`
|
||||
return "图书API";
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
其中`getDocTitle()`返回文档模块名,不能和其它微服务重复。比如订单服务返回:`订单API`;库存服务返回:`库存API`
|
||||
|
||||
- 编写swagger注解
|
||||
|
||||
分别在请求参数和返回结果类中编写`@ApiModelProperty`
|
||||
|
||||
```java
|
||||
// 请求参数
|
||||
@Data
|
||||
public class BookParam {
|
||||
@ApiModelProperty(value = "图书id", example = "1")
|
||||
private int id;
|
||||
|
||||
@ApiModelProperty(value = "图书ISBN", example = "xxxx")
|
||||
private String isbn;
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
@Data
|
||||
public class BookVO {
|
||||
@ApiModelProperty(value = "图书id", example = "1")
|
||||
private int id;
|
||||
|
||||
@ApiModelProperty(value = "图书名称", example = "白雪公主")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "isbn", example = "xxxxxx")
|
||||
private String isbn;
|
||||
}
|
||||
```
|
||||
|
||||
- 在接口方法上编写`@ApiOperation`注解
|
||||
|
||||
```java
|
||||
@ApiOperation(value="查询书本信息", notes = "可以根据ISBN查询书本信息")
|
||||
@ApiMapping(value = "book.search")
|
||||
public BookVO searchBook(BookParam param) {
|
||||
BookVO bookVO = new BookVO();
|
||||
bookVO.setId(1);
|
||||
bookVO.setName("白雪公主,ISBN:" + param.getIsbn());
|
||||
bookVO.setIsbn("ABCSSSSDDD");
|
||||
return bookVO;
|
||||
}
|
||||
```
|
||||
|
||||
其中`value`属性填接口名称,简明扼要。`notes`填写接口的详细信息,介绍,用途,注意事项等。
|
||||
|
||||
## 查看文档
|
||||
|
||||
- 启动website-server(运行WebsiteServerApplication.java)
|
||||
- 找到sop-website/website-front/pages/doc/doc.html,IDEA下右键--Debug
|
||||
|
||||
如果没有IDEA可以下个webstorm,同样有本地静态服务器功能。
|
||||
|
||||
效果图如下
|
||||
|
||||

|
||||
|
||||
## 注解对应关系
|
||||
|
||||
swagger注解和文档界面显示关系如下图所示:
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
@@ -1,61 +0,0 @@
|
||||
# 接口交互详解
|
||||
|
||||
开放平台所提供的接口有几十个到几百个不等,同样支持的服务也是多个的。就拿[支付宝开放平台](https://docs.open.alipay.com/api)来说
|
||||
它所提供的服务有,支付服务、会员服务、店铺服务、芝麻信用服务等。相信这些服务接口肯定不是写在同一个项目中,但是它的接口地址只有一个:https://openapi.alipay.com/gateway.do
|
||||
从地址信息中可以看到,这是一个网关服务。也就是说,网关是所有请求的入口,然后通过请求分发的方式,把请求路由到具体某个服务中去。
|
||||
虽然支付宝开放平台的实现方式我们不得而知,但是这种思路是可行的。
|
||||
|
||||
SOP也是采用这种方式实现,大致步骤如下:
|
||||
|
||||
- 每个服务注册到注册中心,在启动的时候把自己的路由信息上传到zookeeper,并且保证每一个接口都能对应到哪个服务。
|
||||
- 网关启动时同样注册到注册中心,获取zookeeper上的接口信息,保存到本地,并监听zookeeper上的接口信息,一旦接口信息有修改,网关这边能及时进行更新。
|
||||
- 网关收到客户端请求后,先进行签名校验,通过之后根据接口信息找到对应的服务,然后进行路由
|
||||
- 网关对返回结果进行处理(或不处理),返回给客户端。
|
||||
|
||||
如何通过接口参数找到对应的服务呢?
|
||||
|
||||
在网关定义一个`Map<String, RouteInfo> routeMap = ...`,key为接口名+版本号。
|
||||
|
||||
网关启动时,从zookeeper中获取路由信息,并保存到routeMap中
|
||||
|
||||
```java
|
||||
routeMap = buildFromZookeeper();
|
||||
```
|
||||
|
||||
接口请求进来后,根据`方法名+版本号`获取路由信息,然后进行路由转发。
|
||||
|
||||
```java
|
||||
String method = request.getParameter("method");
|
||||
String version = request.getParameter("version");
|
||||
|
||||
RouteInfo routeInfo = routeMap.get(method + version);
|
||||
|
||||
doRoute(routeInfo);
|
||||
```
|
||||
|
||||
因为有多个服务把路由信息注册到zookeeper,我们要确保接口名唯一,即`method`全局唯一。
|
||||
|
||||
我们推荐接口名的命名规则应该是:`服务模块.业务模块.功能模块.行为`,如:
|
||||
|
||||
mini.user.userinfo.get 小程序服务.用户模块.用户信息.获取
|
||||
|
||||
member.register.total.get 会员服务.注册模块.注册总数.获取
|
||||
|
||||
如果觉得命名规则有点长可以精简为:`服务模块.功能模块.行为`,如`member.usercount.get`,前提是确保前缀要有所区分,不和其它服务冲突。
|
||||
|
||||
得益于Spring Cloud的注册中心和和网关功能,我们能很方便的进行接口路由,并且还能实现LoadBalance,不需要自己再去实现。
|
||||
|
||||
整个SOP的架构如下图所示:
|
||||
|
||||

|
||||
|
||||
- 完整请求路线
|
||||
|
||||
```
|
||||
客户端生成签名串 → 客户端发送请求 →【网关签名校验 → 权限校验 → 限流处理 → 路由转发】→ {微服务端业务参数校验 → 处理业务逻辑 → 微服务端返回结果}
|
||||
↓
|
||||
客户端业务处理 ← 客户端验证服务端签名 ← 客户端收到结果 ← -------------【网关返回最终结果 ← 生成服务端签名 ← 网关处理结果】← 结果返回到网关
|
||||
|
||||
【】:表示网关处理
|
||||
{}:表示微服务端处理
|
||||
```
|
||||
@@ -1,62 +0,0 @@
|
||||
# easyopen支持
|
||||
|
||||
SOP对easyopen项目提供了很好的支持,如果您的服务端使用了easyopen框架,相关配置步骤如下:
|
||||
|
||||
## 服务端配置
|
||||
|
||||
首先是服务端相关配置
|
||||
|
||||
- pom添加依赖
|
||||
|
||||
```xml
|
||||
<!-- sop接入依赖 -->
|
||||
<dependency>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-service-common</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.oschina.durcframework</groupId>
|
||||
<artifactId>easyopen</artifactId>
|
||||
<version>1.16.1</version>
|
||||
</dependency>
|
||||
<!-- sop接入依赖 end -->
|
||||
```
|
||||
|
||||
easyopen版本必须升级到1.16.1
|
||||
|
||||
- 启动类上面添加注解@EnableDiscoveryClient,将自己注册到注册中心
|
||||
- 新增一个配置类,继承EasyopenServiceConfiguration,内容为空
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class SopConfig extends EasyopenServiceConfiguration {
|
||||
}
|
||||
```
|
||||
|
||||
服务端配置完毕,重启服务。
|
||||
|
||||
## 网关端配置
|
||||
|
||||
接下来是网关的配置
|
||||
|
||||
- 打开ZuulConfig.java,注释掉原本的@Configuration,新增如下Configuration
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class ZuulConfig extends EasyopenZuulConfiguration {
|
||||
static {
|
||||
new ManagerInitializer();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
配置完毕,重启网关服务,可运行测试用例`EasyopenClientPostTest.java`验证
|
||||
|
||||
**注:** 配置完成后easyopen签名校验将会关闭,改用网关端来校验;网关对easyopen返回的结果不进行处理,直接返回服务端的结果。
|
||||
|
||||
完整配置可查看sop-example/sop-easyopen项目
|
||||
@@ -1,48 +0,0 @@
|
||||
# 使用签名校验工具
|
||||
|
||||
## 生成公私钥
|
||||
|
||||
SOP默认签名算法仿照的是支付宝开放平台,因此我们可以使用支付宝开放平台提供的密钥生成工具,[下载地址](https://docs.open.alipay.com/291/105971/)
|
||||
|
||||
工具下载完后,运行工具
|
||||
|
||||
- 秘钥格式选择:PKCS8(JAVA适用)
|
||||
- 秘钥长度:2048
|
||||
|
||||
然后点击`生成秘钥`,下面文本框会生成,公私钥,如下图所示:
|
||||
|
||||

|
||||
|
||||
公钥给到开放平台,打开sop-gateway项目中的`ZuulConfig.java`,复制公钥
|
||||
|
||||
```java
|
||||
appSecretStore.put(应用ID, 公钥内容);
|
||||
```
|
||||
|
||||
- 应用ID(app_id):建议个格式为`yyyyMMddHHmmss+自增ID`,如2019032617262200001
|
||||
- 公钥内容:刚刚生成的公钥字符串
|
||||
|
||||
接着私钥放入客户端进行调用。参见AlipayClientPostTest类
|
||||
|
||||
## 签名校验
|
||||
|
||||
验证工具切换到`签名`tab页
|
||||
|
||||
例如执行com.gitee.sop.AlipayClientPostTest.testPost()方法,控制台会打印如下信息:
|
||||
|
||||
```
|
||||
----------- 请求信息 -----------
|
||||
请求参数:charset=utf-8&biz_content={"name":"葫芦娃","id":"1"}&method=alipay.story.get&format=json&app_id=2019032617262200001&sign_type=RSA2&version=1.0×tamp=2019-03-26 17:37:41
|
||||
商户秘钥:MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=
|
||||
待签名内容:app_id=2019032617262200001&biz_content={"name":"葫芦娃","id":"1"}&charset=utf-8&format=json&method=alipay.story.get&sign_type=RSA2×tamp=2019-03-26 17:37:41&version=1.0
|
||||
签名(sign):JCZMSFkXSjw/4TokyM9/9shyrMl7KxQGIZDHIm7+Bvl49Z816/iF/xXLYjUiPXWAXYfp+HlEs3VVQp1Kjh4tIKuKX/i1+exNVs+ICcqVGBewPSZwiWHGpZTfEUiYOoPyUL/eoRIj7Mvlaow0sI9uP7NXNo0kxEFjUOMCzZA7eKm/pu2FHRXt4OhgXq2Go30K5a9oCbbMc/2xcQCc2+zwvOgV3o0A6eMyeAXDJW+eQ2KLhtlqPQvbRV+xyfSut7TkwYSEuNXVVQAfN2lwAS3ru9CQIs8Uz7lK1ITkLu80yLapZVL7tS1PdxK0e3QYToCWD43Wtuoow4ZdDwwzir90HQ==
|
||||
----------- 返回结果 -----------
|
||||
{"alipay_story_get_response":{"msg":"Success","code":"10000","name":"海底小纵队(alipay.story.get)","id":1},"sign":"JCZMSFkXSjw/4TokyM9/9shyrMl7KxQGIZDHIm7+Bvl49Z816/iF/xXLYjUiPXWAXYfp+HlEs3VVQp1Kjh4tIKuKX/i1+exNVs+ICcqVGBewPSZwiWHGpZTfEUiYOoPyUL/eoRIj7Mvlaow0sI9uP7NXNo0kxEFjUOMCzZA7eKm/pu2FHRXt4OhgXq2Go30K5a9oCbbMc/2xcQCc2+zwvOgV3o0A6eMyeAXDJW+eQ2KLhtlqPQvbRV+xyfSut7TkwYSEuNXVVQAfN2lwAS3ru9CQIs8Uz7lK1ITkLu80yLapZVL7tS1PdxK0e3QYToCWD43Wtuoow4ZdDwwzir90HQ=="}
|
||||
```
|
||||
|
||||
字符集选UTF-8,签名方式RSA2
|
||||
|
||||
把控制台中的`请求参数`和`商户秘钥`填入文本框中,然后点击`开始签名`,下方会出现待签名内容和sign。
|
||||
|
||||
通过比对判断签名过程是否正确。
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# ISV管理
|
||||
|
||||
ISV:独立软体开发商(independent software vendor),即接入方或者说接口调用者,在SOP中称为ISV。
|
||||
|
||||
---
|
||||
|
||||
在1.1.0版本中新增了ISV管理功能,在sop-admin中ISV管理模块下。功能如下:
|
||||
|
||||
- 基本信息的增查改
|
||||
- 设置对应角色
|
||||
|
||||
界面如下图所示:
|
||||
|
||||

|
||||
|
||||
## 秘钥管理
|
||||
|
||||
点击操作列的`秘钥管理`,可对ISV的秘钥进行设置。
|
||||
|
||||
- 如果采用淘宝开放平台签名方式,签名方式选择`MD5`,如果采用支付宝开放平台签名方式,选择`RSA`
|
||||
- 如果对接的开发者使用非Java语言,秘钥格式选择`PKCS1`
|
||||
- 带 ★ 的分配给开发者
|
||||
|
||||

|
||||
@@ -1,23 +0,0 @@
|
||||
# 自定义路由
|
||||
|
||||
假设有一个非java开发的接口,比如php开发的接口,然后需要接入到SOP,使用接口名版本号进行路由跳转。
|
||||
|
||||
操作方式如下:
|
||||
|
||||
- 登录sop-admin,服务管理--路由管理
|
||||
- 新建一个自定义服务:`php-service`
|
||||
|
||||

|
||||
|
||||
- 选中php-service,新建一个路由,输入接口名、版本号、uri
|
||||
|
||||

|
||||
|
||||
- 请求网关
|
||||
|
||||
`http://localhost:8081/api?method=php.goods.list&version=1.0`会跳转到`http://www.xxx.com/api/listGoods.php`
|
||||
|
||||
注意:
|
||||
|
||||
- php接口返回数据必须是json格式,返回其它内容可能会调用失败
|
||||
- 只有自定义服务、路由可以删除
|
||||
@@ -1,125 +0,0 @@
|
||||
# 自定义返回结果
|
||||
|
||||
网关默认对业务结果进行合并,然后返回统一的格式。
|
||||
|
||||
针对`alipay.story.find`接口,微服务端返回结果如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "白雪公主",
|
||||
"id": 1,
|
||||
"gmtCreate": 1554193987378
|
||||
}
|
||||
```
|
||||
|
||||
网关合并后,最终结果如下
|
||||
|
||||
```json
|
||||
{
|
||||
"alipay_story_find_response": {
|
||||
"msg": "Success",
|
||||
"code": "10000",
|
||||
"name": "白雪公主",
|
||||
"id": 1,
|
||||
"gmtCreate": 1554193987378
|
||||
},
|
||||
"sign": "xxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
其中`alipay_story_find_response`是它的数据节点。规则是:
|
||||
|
||||
> 将接口名中的点`.`转换成下划线`_`,后面加上`_response`
|
||||
|
||||
代码实现如下:
|
||||
|
||||
```java
|
||||
String method = "alipay.story.find";
|
||||
return method.replace('.', '_') + "_response";
|
||||
```
|
||||
|
||||
详见`DefaultDataNameBuilder.java`
|
||||
|
||||
如果要更改数据节点,比如`result`,可使用`CustomDataNameBuilder.java`。
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class ZuulConfig extends AlipayZuulConfiguration {
|
||||
|
||||
static {
|
||||
...
|
||||
ApiConfig.getInstance().setDataNameBuilder(new CustomDataNameBuilder());
|
||||
...
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
设置后,网关统一的返回结果如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
...
|
||||
},
|
||||
"sign": "xxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
此外,构造方法可指定自定义字段名称:`new CustomDataNameBuilder("data");`。
|
||||
设置后,数据节点将变成`data`
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
...
|
||||
},
|
||||
"sign": "xxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
**注**:网关设置了CustomDataNameBuilder后,SDK也要做相应的更改:`SdkConfig.dataNameBuilder = new CustomDataNameBuilder();`
|
||||
|
||||
## 自定义结果处理
|
||||
|
||||
如果想要对微服务结果做更深一步处理,步骤如下:
|
||||
|
||||
1. 新增一个类,继承`ZuulResultExecutor.java`,并重写`public String merge(T exchange, JSONObject responseData)`方法
|
||||
|
||||
方法merge参数说明如下:
|
||||
|
||||
exchange:RequestContext对象
|
||||
responseData:微服务端返回的结果
|
||||
|
||||
方法返回最终结果
|
||||
|
||||
2. 配置自定义类
|
||||
|
||||
```java
|
||||
public class SopGatewayApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
ApiConfig.getInstance().setZuulResultExecutor(new MyzuulResultExecutor());
|
||||
SpringApplication.run(SopGatewayApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 不合并结果
|
||||
|
||||
如果不希望对结果进行合并,可设置`ApiConfig.getInstance().setMergeResult(false);`
|
||||
|
||||
```java
|
||||
public class SopGatewayApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
ApiConfig.getInstance().setMergeResult(false);
|
||||
SpringApplication.run(SopGatewayApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
这样,网关最终返回结果即为微服务端的返回结果。
|
||||
@@ -1,105 +0,0 @@
|
||||
# 自定义过滤器
|
||||
|
||||
## zuul过滤器
|
||||
|
||||
zuul过滤器列表如下:
|
||||
|
||||
| 类型 | 顺序 | 过滤器 | 功能 |
|
||||
| ----- | ---- | ----------------------- | ---------------------------- |
|
||||
| pre | -1000 | PreValidateFilter (SOP自带) | 校验签名 |
|
||||
| pre | -999 | PreRoutePermissionFilter (SOP自带) | 路由权限校验,有些接口需要配置权限才能访问 |
|
||||
| pre | -998 | PreLimitFilter (SOP自带) | 限流拦截器 |
|
||||
| pre | -3 | ServletDetectionFilter | 标记处理 Servlet 的类型 |
|
||||
| pre | -2 | Servlet30WrapperFilter | 包装 HttpServletRequest 请求 |
|
||||
| pre | -1 | FormBodyWrapperFilter | 包装请求体 Servlet30WrapperFilter |
|
||||
| pre | 1 | DebugFilter | 标记调试标志 |
|
||||
| pre | 5 | PreDecorationFilter | 决定路由转发过滤器 |
|
||||
| route | 10 | RibbonRoutingFilter | serviceId 请求转发 |
|
||||
| route | 100 | SimpleHostRoutingFilter | url 请求转发 |
|
||||
| route | 500 | SendForwardFilter | forward 请求转发 |
|
||||
| post | 0 | SendErrorFilter | 处理有错误的请求响应 |
|
||||
| post | 1000 | SendResponseFilter | 处理正常的请求响应 |
|
||||
|
||||
顺序值小的优先执行,`-3`之前是sop自带的过滤器,`-3`开始是zuul自带的过滤器。
|
||||
|
||||
创建自定义过滤器可以从`-500`开始(-1000 ~ -501留给SOP)。下面是一个自定义过虑器的例子:
|
||||
|
||||
```java
|
||||
public class PreXXXFilter extends BaseZuulFilter {
|
||||
@Override
|
||||
protected FilterType getFilterType() {
|
||||
return FilterType.PRE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFilterOrder() {
|
||||
return -500;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doRun(RequestContext requestContext) throws ZuulException {
|
||||
HttpServletRequest request = requestContext.getRequest();
|
||||
ApiParam apiParam = ZuulContext.getApiParam();
|
||||
String appKey = apiParam.fetchAppKey();
|
||||
// ...业务处理
|
||||
|
||||
// 固定返回null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
过滤器编写完毕后,在Config中使用:
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class ZuulConfig extends AlipayZuulConfiguration {
|
||||
|
||||
...
|
||||
|
||||
@Bean
|
||||
PreXXXFilter preXXXFilter() {
|
||||
return new PreXXXFilter();
|
||||
}
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## spring cloud gateway
|
||||
|
||||
跟zuul同理
|
||||
|
||||
```java
|
||||
public class XXXFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ApiParam apiParam = (ApiParam)exchange.getAttribute(SopConstants.CACHE_API_PARAM);
|
||||
String appKey = apiParam.fetchAppKey();
|
||||
// ...业务处理
|
||||
...
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return -500;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
使用过滤器:
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class GatewayConfig extends AlipayGatewayConfiguration {
|
||||
|
||||
...
|
||||
@Bean
|
||||
XXXFilter xxxFilter() {
|
||||
return new XXXFilter();
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
@@ -1,25 +0,0 @@
|
||||
# 路由授权
|
||||
|
||||
1.1.0版本新增了路由授权功能,采用RBAC权限管理方式实现。
|
||||
|
||||
- 每个ISV(appKey)对应一个或多个角色
|
||||
- 每个角色分配多个路由权限
|
||||
|
||||
接口跟角色相关联,ISV拥有哪些角色,就具有角色对应的接口访问权限。
|
||||
|
||||
假设把路由a,b,c分配给了`VIP角色`,那么具有VIP角色的ISV可以访问a,b,c三个路由。
|
||||
|
||||
默认情况下,接口访问时公开的,ISV都能访问。如果要设置某个接口访问权限,在`@ApiMapping`注解中指定permission=true。
|
||||
如:`@ApiMapping(value = "permission.story.get", permission = true)`。这样该接口是需要经过授权给ISV才能访问的。
|
||||
|
||||
重启服务后,登录admin,服务管理-路由列表界面中,操作操作列会出现一个授权按钮,点击出现授权窗口,勾选对应的角色即可完成授权。
|
||||
|
||||
- 点击`授权`按钮,进行角色授权
|
||||
|
||||

|
||||
|
||||
- 勾选对应角色,点击保存
|
||||
|
||||

|
||||
|
||||
这里演示的是:具有普通权限的ISV能够访问`permission.story.get`接口,运行`PermissionDemoPostTest`测试用例进行验证
|
||||
@@ -1,44 +0,0 @@
|
||||
# 接口限流
|
||||
|
||||
SOP提供了简单的接口限流策略:
|
||||
|
||||
- 漏桶策略:每秒处理固定数量的请求,超出请求返回错误信息。
|
||||
- 令牌桶策略:每秒放置固定数量的令牌数,每个请求进来后先去拿令牌,拿到了令牌才能继续,拿不到则等候令牌重新生成了再拿。
|
||||
|
||||
如果一个接口设置了漏桶策略,假设接口每秒可处理5个请求,一秒内同时有6个请求进来,前5个接口是能够访问的,第六个请求将返回错误信息。
|
||||
|
||||
如果设置了令牌桶策略,桶的容量是5,那么每秒中生成5个令牌,同一时间有6个请求进来,那么前5个能成功拿到令牌继续,第六个则等待,令牌重新生成了再拿。
|
||||
|
||||
默认情况下接口的限流功能是关闭的,可在sop admin中配置并开启。功能在`路由管理-->限流管理`下。
|
||||
|
||||
## 新版限流(1.9.0)
|
||||
|
||||
1.9.0将之前的限流进行了改造,新的限流支持更多的限流方式。之前只能针对某个接口限流,新版限流可以在`路由ID(接口)、appKey、ip`上进行限流。
|
||||
|
||||
- 可针对接口进行限流,所有访问该接口的请求都被限流
|
||||
- 可针对appKey进行限流,某个appKey请求过来后,对他限流
|
||||
- 可针对IP进行限流,某个IP请求过来后,对他限流
|
||||
|
||||
此外还可以进行组合
|
||||
|
||||
- 可针对接口+appKey进行限流,这个appKey调用某个接口比较频繁,可以将它限制住
|
||||
- 可针对接口+IP进行限流,某个ip在频繁调用接口,可以将它限制住
|
||||
|
||||
由于存在组合情况,一个接口可能会配置多个限流规则。在这种情况下会优先取排序值小的那一条,如果排序值一样,则默认取第一条。
|
||||
|
||||
假设有下面几个限流规则:
|
||||
|
||||
- 接口:`goods.get`, 排序值:1, 每秒可处理请求数:10
|
||||
- 接口:`goods.get`, appKey:xxxx, 排序值:0, 每秒可处理请求数:5
|
||||
- 接口:`goods.get`, ip:172.1.2.2, 排序值:2, 每秒可处理请求数:6
|
||||
|
||||
客户端调用接口:`http://open.domain.com/api?method=goods.get&app_key=xxxx`,客户端IP为`172.1.2.2`
|
||||
|
||||
这种情况下上面三条限流规则都命中了,由于排序值小优先执行,因此第二条规则命中.
|
||||
|
||||
|
||||
具体设置方式可在sop admin中配置,功能在`服务管理-->限流管理`下。执行`sop-test/src/test/java/com/gitee/sop/LimitDemoPostTest.java`测试用例验证限流情况
|
||||
|
||||

|
||||
|
||||

|
||||
@@ -1,44 +0,0 @@
|
||||
# 监控日志
|
||||
|
||||
1.10.0开始sop-admin提供了简单的监控日志查询,方便在线排查问题。
|
||||
|
||||
- 错误日志统一在网关负责收集
|
||||
- 只收集未知类型的错误日志,开发人员主动throw的异常不收集
|
||||
- sop-admin通过网关提供的restful接口获取日志内容,然后在后台展示
|
||||
- 收集的日志存放在内存中,重启网关日志会消失
|
||||
- 只会收集20条不同的日志内容,相同内容会count+1。可设置`ApiConfig.storeErrorCapacity`属性扩大容量,默认容量20
|
||||
|
||||
第一次使用需要添加网关服务器实例,前往:`服务管理--监控日志--添加监控服务器`
|
||||
|
||||
## 永久保存日志
|
||||
|
||||
默认收集的日志存放在内存中,重启网关日志会消失(见:`DefaultServiceErrorManager.java`)。如果要永久保存日志内容,需要自己实现`ServiceErrorManager`接口
|
||||
|
||||
```java
|
||||
public class MyServiceErrorManager implements ServiceErrorManager {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
然后在ApiConfig中配置
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class ZuulConfig extends AlipayZuulConfiguration {
|
||||
|
||||
static {
|
||||
...
|
||||
ApiConfig.getInstance().setServiceErrorManager(new MyServiceErrorManager());
|
||||
// 日志收集容量,默认20。只会收集20条不同内容的日志
|
||||
ApiConfig.getInstance().setStoreErrorCapacity(20);
|
||||
...
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
- 后台预览
|
||||
|
||||

|
||||
|
||||

|
||||
@@ -1,335 +0,0 @@
|
||||
# 开发SDK
|
||||
|
||||
开放平台把接口开发完毕后,一般需要开发对应的SDK,提供给ISV。SOP提供了一个基础的SDK开发包
|
||||
|
||||
开发者可以在此基础上做开发,就拿sdk-java来说,具体步骤如下:
|
||||
|
||||
## sdk-java
|
||||
|
||||
SDK依赖了三个jar包
|
||||
|
||||
- okhttp.jar 用于网络请求
|
||||
- fastjson.jar 用于json处理
|
||||
- commons-logging.jar 日志处理
|
||||
|
||||
### 接口封装步骤
|
||||
|
||||
比如获取故事信息接口
|
||||
|
||||
- 接口名:alipay.story.find
|
||||
- 版本号:1.0
|
||||
- 参数:name 故事名称
|
||||
- 返回信息
|
||||
|
||||
```json
|
||||
{
|
||||
"alipay_story_find_response": {
|
||||
"msg": "Success",
|
||||
"code": "10000",
|
||||
"name": "白雪公主",
|
||||
"id": 1,
|
||||
"gmtCreate": 1554193987378
|
||||
},
|
||||
"sign": "xxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
针对这个接口,封装步骤如下:
|
||||
|
||||
1.在`model`包下新建一个类,定义业务参数
|
||||
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class GetStoryModel {
|
||||
|
||||
@JSONField(name = "name")
|
||||
private String name;
|
||||
}
|
||||
```
|
||||
|
||||
2.在`response`包下新建一个返回类GetStoryResponse,继承`BaseResponse`
|
||||
|
||||
里面填写返回的字段
|
||||
|
||||
```
|
||||
@Data
|
||||
public class GetStoryResponse extends BaseResponse {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Date gmtCreate;
|
||||
}
|
||||
```
|
||||
|
||||
3.在`request`包下新建一个请求类,继承`BaseRequest`
|
||||
|
||||
BaseRequest中有个泛型参数,填`GetStoryResponse`类,表示这个请求对应的返回类。
|
||||
重写`method()`方法,填接口名。
|
||||
|
||||
如果要指定版本号,可重写`version()`方法,或者后续使用`request.setVersion(version)`进行设置
|
||||
|
||||
```java
|
||||
public class GetStoryRequest extends BaseRequest<GetStoryResponse> {
|
||||
@Override
|
||||
protected String method() {
|
||||
return "alipay.story.find";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
可重写`getRequestMethod()`方法指定HTTP请求method,默认是POST。
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected RequestMethod getRequestMethod() {
|
||||
return RequestMethod.GET;
|
||||
}
|
||||
```
|
||||
|
||||
**建议读请求用GET,写请求用POST**,
|
||||
|
||||
### 使用方式
|
||||
|
||||
```java
|
||||
String url = "http://localhost:8081/api"; // zuul
|
||||
String appId = "2019032617262200001";
|
||||
String privateKey = "你的私钥";
|
||||
|
||||
// 声明一个就行
|
||||
OpenClient client = new OpenClient(url, appId, privateKey);
|
||||
|
||||
// 标准用法
|
||||
@Test
|
||||
public void testGet() {
|
||||
// 创建请求对象
|
||||
GetStoryRequest request = new GetStoryRequest();
|
||||
// 请求参数
|
||||
GetStoryModel model = new GetStoryModel();
|
||||
model.setName("白雪公主");
|
||||
|
||||
request.setBizModel(model);
|
||||
|
||||
// 发送请求
|
||||
GetStoryResponse response = client.execute(request);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
// 返回结果
|
||||
System.out.println(String.format("成功!response:%s\n响应原始内容:%s",
|
||||
JSON.toJSONString(response), response.getBody()));
|
||||
} else {
|
||||
System.out.println("错误,subCode:" + response.getSubCode() + ", subMsg:" + response.getSubMsg());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用方式2(懒人版)
|
||||
|
||||
如果不想添加Request,Response,Model。可以用这种方式,返回body部分是字符串,后续自己处理
|
||||
|
||||
body对应的是alipay_story_find_response部分
|
||||
|
||||
```java
|
||||
@Test
|
||||
public void testLazy() {
|
||||
// 创建请求对象
|
||||
CommonRequest request = new CommonRequest("alipay.story.find");
|
||||
// 请求参数
|
||||
Map<String, Object> bizModel = new HashMap<>();
|
||||
bizModel.put("name", "白雪公主");
|
||||
request.setBizModel(bizModel);
|
||||
|
||||
// 发送请求
|
||||
CommonResponse response = client.execute(request);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
// 返回结果,body对应的是alipay_story_find_response部分
|
||||
String body = response.getBody();
|
||||
JSONObject jsonObject = JSON.parseObject(body);
|
||||
System.out.println(jsonObject);
|
||||
} else {
|
||||
System.out.println(response);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## sdk-.net
|
||||
|
||||
|
||||
### 接口封装步骤
|
||||
|
||||
比如获取故事信息接口
|
||||
|
||||
- 接口名:alipay.story.find
|
||||
- 版本号:1.0
|
||||
- 参数:name 故事名称
|
||||
- 返回信息
|
||||
|
||||
```
|
||||
{
|
||||
"alipay_story_find_response": {
|
||||
"msg": "Success",
|
||||
"code": "10000",
|
||||
"name": "白雪公主",
|
||||
"id": 1,
|
||||
"gmtCreate": 1554193987378
|
||||
},
|
||||
"sign": "xxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
针对这个接口,封装步骤如下:
|
||||
|
||||
1.在`Model`包下新建一个类,定义业务参数
|
||||
|
||||
```
|
||||
namespace SDKCSharp.Model
|
||||
{
|
||||
public class GetStoryModel
|
||||
{
|
||||
/// <summary>
|
||||
/// 故事名称
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`[JsonProperty("name")]`是Newtonsoft.Json组件中的类,用于Json序列化,括号中的是参数名称。
|
||||
类似于Java中的注解,`@JSONField(name = "xx")`
|
||||
|
||||
2.在`Response`包下新建一个返回类GetStoryResponse,继承`BaseResponse`
|
||||
|
||||
里面填写返回的字段
|
||||
|
||||
```
|
||||
namespace SDKCSharp.Response
|
||||
{
|
||||
public class GetStoryResponse: BaseResponse
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("gmt_create")]
|
||||
public string GmtCreate { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
3.在`Request`文件夹下新建一个请求类,继承`BaseRequest`
|
||||
|
||||
BaseRequest中有个泛型参数,填`GetStoryResponse`类,表示这个请求对应的返回类。
|
||||
重写`GetMethod()`方法,填接口名。
|
||||
|
||||
如果要指定版本号,可重写`GetVersion()`方法,或者后续使用`request.Version = version`进行设置
|
||||
|
||||
```
|
||||
namespace SDKCSharp.Request
|
||||
{
|
||||
public class GetStoryRequest : BaseRequest<GetStoryResponse>
|
||||
{
|
||||
public override string GetMethod()
|
||||
{
|
||||
return "alipay.story.find";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 使用方式
|
||||
|
||||
```
|
||||
class MainClass
|
||||
{
|
||||
static string url = "http://localhost:8081/api"; // zuul
|
||||
static string appId = "2019032617262200001";
|
||||
// 支付宝私钥
|
||||
static string privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=";
|
||||
|
||||
|
||||
// 声明一个就行
|
||||
static OpenClient client = new OpenClient(url, appId, privateKey);
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
TestGet();
|
||||
}
|
||||
|
||||
// 标准用法
|
||||
private static void TestGet()
|
||||
{
|
||||
// 创建请求对象
|
||||
GetStoryRequest request = new GetStoryRequest();
|
||||
// 请求参数
|
||||
GetStoryModel model = new GetStoryModel();
|
||||
model.Name = "白雪公主";
|
||||
request.BizModel = model;
|
||||
|
||||
// 发送请求
|
||||
GetStoryResponse response = client.Execute(request);
|
||||
|
||||
if (response.IsSuccess())
|
||||
{
|
||||
// 返回结果
|
||||
Console.WriteLine("故事名称:{0}", response.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("错误, code:{0}, msg:{1}, subCode:{2}, subMsg:{3}",
|
||||
response.Code, response.Msg, response.SubCode, response.SubMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### 使用方式2(懒人版)
|
||||
|
||||
如果不想添加Request,Response,Model。可以用这种方式,返回data部分是Dictionary<string, object>,后续自己处理
|
||||
|
||||
```
|
||||
// 懒人版,如果不想添加Request,Response,Model。可以用这种方式,返回Dictionary<string, object>,后续自己处理
|
||||
private static void TestCommon()
|
||||
{
|
||||
// 创建请求对象
|
||||
CommonRequest request = new CommonRequest("alipay.story.find");
|
||||
// 请求参数
|
||||
Dictionary<string, string> bizModel = new Dictionary<string, string>
|
||||
{
|
||||
["name"] = "白雪公主"
|
||||
};
|
||||
|
||||
request.BizModel = bizModel;
|
||||
|
||||
// 发送请求
|
||||
CommonResponse response = client.Execute(request);
|
||||
|
||||
if (response.IsSuccess())
|
||||
{
|
||||
// 返回结果
|
||||
string body = response.Body;
|
||||
Dictionary<string, object> dict = JsonUtil.ParseToDictionary(body);
|
||||
|
||||
Console.WriteLine("Dictionary内容:");
|
||||
foreach (var item in dict)
|
||||
{
|
||||
Console.WriteLine("{0}:{1}", item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("错误, code:{0}, msg:{1}, subCode:{2}, subMsg:{3}",
|
||||
response.Code, response.Msg, response.SubCode, response.SubMsg);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,75 +0,0 @@
|
||||
# 使用SpringCloudGateway
|
||||
|
||||
SOP默认网关是使用Spring Cloud Zuul,您也可以切换成Spring Cloud Gateway,完整代码见`SpringCloudGateway`分支。
|
||||
|
||||
**注:**:SOP对Spring Cloud Gateway的支持目前处于beta阶段,推荐使用zuul。
|
||||
|
||||
步骤如下:
|
||||
|
||||
- 打开sop-gateway/pom.xml,注释spring cloud zuul依赖,打开Spring Cloud Gateway依赖
|
||||
|
||||
```xml
|
||||
<!-- ↓↓↓ 使用spring cloud zuul ↓↓↓
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- ↑↑↑ 使用spring cloud zuul ↑↑↑ -->
|
||||
|
||||
|
||||
<!-- ↓↓↓ 使用spring cloud gateway,处于beta阶段,推荐使用zuul ↓↓↓ -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- ↑↑↑ 使用spring cloud gateway ↑↑↑ -->
|
||||
```
|
||||
|
||||
- 打开启动类`SopGatewayApplication.java`, 注释zuul相关注解
|
||||
|
||||
```java
|
||||
package com.gitee.sop.gateway;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
//import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
|
||||
// 开启网关功能
|
||||
//@EnableZuulProxy
|
||||
@SpringBootApplication
|
||||
public class SopGatewayApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SopGatewayApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
- 禁用ZuulConfig类,注释掉@Configuration注解即可
|
||||
|
||||
```java
|
||||
//@Configuration
|
||||
public class ZuulConfig extends AlipayZuulConfiguration {...}
|
||||
```
|
||||
|
||||
- 启用GatewayConfig类,打开@Configuration注释
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class GatewayConfig extends AlipayGatewayConfiguration {...}
|
||||
```
|
||||
|
||||
修改完毕,重启sop-gateway
|
||||
|
||||
运行SpringCloudGatewayClientPostTest测试用例。
|
||||
@@ -1,117 +0,0 @@
|
||||
# 应用授权
|
||||
|
||||
## 概述
|
||||
|
||||
- 1、用户对开发者进行应用授权后,开发者可以帮助用户完成相应的业务逻辑。
|
||||
- 2、授权采用标准的OAuth 2.0流程。
|
||||
|
||||
## 授权流程
|
||||
|
||||

|
||||
|
||||
## 快速接入
|
||||
|
||||
- 第一步:应用授权URL拼装
|
||||
|
||||
拼接规则:
|
||||
|
||||
http://openauth.yourdomain.com/oauth2/appToAppAuth?app_id=2019032617262200001&redirect_uri=http%3a%2f%2flocalhost%3a8087%2foauth2callback
|
||||
|
||||
参数说明:
|
||||
|
||||
| 参数 | 参数名称 | 类型 | 必填 | 描述 | 范例 |
|
||||
|--------------|-------------|--------|----|---------------|--------------------------|
|
||||
| app_id | 开发者应用的AppId | String | 是 | 开发者应用的AppId | 2015101400446982 |
|
||||
| redirect_uri | 回调页面 | String | 是 | 参数需要UrlEncode | http%3A%2F%2Fexample.com |
|
||||
|
||||
|
||||
- 第二步:获取code
|
||||
|
||||
授权成功后,会跳转至开发者定义的回调页面(即redirect_uri参数对应的url),在回调页面请求中会带上当次授权的授权码code和开发者的app_id,示例如下:
|
||||
|
||||
http://www.xxx.com/oauth2callback?app_id=2015101400446982&code=ca34ea491e7146cc87d25fca24c4cD11
|
||||
|
||||
|
||||
- 第三步:使用code换取app_auth_token
|
||||
|
||||
接口名称:open.auth.token.app
|
||||
|
||||
开发者通过code可以换取app_auth_token、授权用户的userId。
|
||||
|
||||
**注意**:应用授权的code唯一,code使用一次后失效,有效期24小时; app_auth_token永久有效。
|
||||
|
||||
**请求参数说明**
|
||||
|
||||
| 参数 | 参数名称 | 类型 | 必填 | 描述 | 范例 |
|
||||
|---------------|------|--------|----|---------------------------------------------------------------------------------|------------------------------------------|
|
||||
| grant_type | 授权类型 | String | 是 | 如果使用code换取token,则为authorization_code,如果使用refresh_token换取新的token,则为refresh_token | authorization_code |
|
||||
| code | 授权码 | String | 否 | 与refresh_token二选一,用户对应用授权后得到,即第一步中开发者获取到的code值 | bf67d8d5ed754af297f72cc482287X62 |
|
||||
| refresh_token | 刷新令牌 | String | 否 | 与code二选一,可为空,刷新令牌时使用 | 201510BB0c409dd5758b4d939d4008a525463X62 |
|
||||
|
||||
**接口请求SDK示例**
|
||||
|
||||
```java
|
||||
@GetMapping("oauth2callback")
|
||||
@ResponseBody
|
||||
public String callback(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
|
||||
String app_id = servletRequest.getParameter("app_id");
|
||||
String code = servletRequest.getParameter("code");
|
||||
OpenAuthTokenAppRequest request = new OpenAuthTokenAppRequest();
|
||||
OpenAuthTokenAppModel model = new OpenAuthTokenAppModel();
|
||||
model.setCode(code);
|
||||
model.setGrant_type("authorization_code");
|
||||
request.setBizModel(model);
|
||||
// 根据code获取token
|
||||
OpenAuthTokenAppResponse response = openClient.execute(request);
|
||||
if (response.isSuccess()) {
|
||||
// 成功拿到token,开发者在这里保存token信息
|
||||
// 后续使用token进行接口访问
|
||||
log.info("授权成功,body:{}", response.getBody());
|
||||
}
|
||||
return response.getBody();
|
||||
}
|
||||
```
|
||||
|
||||
**同步响应参数说明**
|
||||
|
||||
| 参数 | 参数名称 | 类型 | 必填 | 描述 | 范例 |
|
||||
|-------------------|---------|--------|----|----------------------------------------------------------|----------------------------------|
|
||||
| app_auth_token | 用户授权令牌 | String | 是 | 通过该令牌来帮助用户发起请求,完成业务 | 856faf8d77d3b985c1073557ce6ea724 |
|
||||
| user_id | 授权用户的ID | String | 是 | 授权者id | 1 |
|
||||
| expires_in | 令牌有效期 | Long | 是 | 负值表示永久有效 | -1 |
|
||||
| re_expires_in | 刷新令牌有效期 | Long | 是 | 负值表示永久有效 | -3 |
|
||||
| app_refresh_token | 刷新令牌时使用 | String | 是 | 刷新令牌后,我们会保证老的app_auth_token从刷新开始10分钟内可继续使用,请及时替换为最新token | 88e68196d2359667f0dc8136e6c86803 |
|
||||
|
||||
|
||||
**同步响应结果示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"open_auth_token_app_response": {
|
||||
"code": "10000",
|
||||
"msg": "Success",
|
||||
"app_auth_token": "88e68196d2359667f0dc8136e6c86803",
|
||||
"app_refresh_token": "856faf8d77d3b985c1073557ce6ea724",
|
||||
"expires_in": -1,
|
||||
"re_expires_in": -3,
|
||||
"user_id": "1"
|
||||
},
|
||||
"sign": "xxx"
|
||||
}
|
||||
```
|
||||
|
||||
**刷新token**
|
||||
|
||||
```java
|
||||
OpenAuthTokenAppRequest request = new OpenAuthTokenAppRequest();
|
||||
OpenAuthTokenAppModel model = new OpenAuthTokenAppModel();
|
||||
model.setGrant_type("refresh_token");
|
||||
model.setRefresh_token("856faf8d77d3b985c1073557ce6ea724");
|
||||
request.setBizModel(model);
|
||||
OpenAuthTokenAppResponse response = openClient.execute(request);
|
||||
if (response.isSuccess()) {
|
||||
// 成功拿到token,开发者在这里保存token信息
|
||||
// 后续使用token进行接口访问
|
||||
log.info("换取token成功,body:{}", response.getBody());
|
||||
}
|
||||
```
|
||||
@@ -1,182 +0,0 @@
|
||||
# 传统web开发
|
||||
|
||||
SOP既可以作为网关服务开发,又可以作为传统的webapp服务开发,传统web开发意思是像普通的web开发那样提供restful接口,没有签名校验功能。
|
||||
|
||||
本篇介绍如何使用SOP进行传统web服务开发,即对接前端应用(H5、小程序、App)。
|
||||
|
||||
- 网关ZuulConfig继承WebappZuulConfiguration类
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class ZuulConfig extends WebappZuulConfiguration {
|
||||
|
||||
static {
|
||||
new ManagerInitializer();
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
设置完毕,网关不在进行签名验证,网关统一的返回结果如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 微服务OpenServiceConfig继承WebappServiceConfiguration类
|
||||
|
||||
```java
|
||||
public class OpenServiceConfig extends WebappServiceConfiguration {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
其它内容不变
|
||||
|
||||
- 前端app请求网关
|
||||
|
||||
请求格式为:`http://ip:port/rest/your_path`,其中`http://ip:port/rest/`为固定部分,后面跟微服务请求路径。
|
||||
|
||||
注意:为了确保各个微服务路径不冲突,必须保证类上方定义的`@RequestMapping`内容唯一,不与其它微服务重复。
|
||||
|
||||
下面是一个微服务的接口例子
|
||||
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("food")
|
||||
public class TraditionalWebappController {
|
||||
@ApiMapping(value = "getFoodById", method = RequestMethod.GET)
|
||||
public Food getFoodById(Integer id) {
|
||||
Food food = new Food();
|
||||
food.setId(id);
|
||||
food.setName("香蕉");
|
||||
food.setPrice(new BigDecimal(20.00));
|
||||
return food;
|
||||
}
|
||||
|
||||
// 加版本号
|
||||
@ApiMapping(value = "getFoodById", method = RequestMethod.GET, version = "1.1")
|
||||
public Food getFoodById2(Integer id) {
|
||||
Food food = new Food();
|
||||
food.setId(id);
|
||||
food.setName("香蕉2");
|
||||
food.setPrice(new BigDecimal(22.00));
|
||||
return food;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这是一个`食品服务`例子,假设网关ip为10.0.1.11,端口8081;食品服务ip为10.0.1.22,端口2222
|
||||
|
||||
1. 网关访问:`http://10.0.1.11:8081/rest/food/getFoodById?id=2`。加版本号:`http://localhost:8081/rest/food/getFoodById?id=2&version=1.1`
|
||||
|
||||
2. 本地访问:`http://10.0.1.22:2222/food/getFoodById/?id=2`
|
||||
|
||||
更多例子,可查看源码类:`TraditionalWebappController.java`
|
||||
|
||||
由此可见,对于前端调用者来说,它把网关看做一个大服务,只访问网关提供的请求,不需要关心网关后面的路由转发。网关后面各个微服务独自管理,
|
||||
微服务之间的调用可以使用dubbo或feign,有了版本号的管理,可以做到服务的平滑升级,对用户来说都是无感知的。结合SOP-Admin提供的上下线功能,
|
||||
可实现预发布环境功能。
|
||||
|
||||
- 封装请求工具【可选】
|
||||
|
||||
封装请求,方便调用,针对vue的封装如下:
|
||||
|
||||
```js
|
||||
import axios from 'axios'
|
||||
|
||||
// 创建axios实例
|
||||
const client = axios.create({
|
||||
baseURL: process.env.BASE_API, // api 的 base_url
|
||||
timeout: 5000, // 请求超时时间
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
})
|
||||
|
||||
const RequestUtil = {
|
||||
/**
|
||||
* 请求接口
|
||||
* @param url 请求路径,如http://localhost:8081/rest/food/getFoodById
|
||||
* @param data 请求数据,json格式
|
||||
* @param callback 成功回调
|
||||
* @param errorCallback 失败回调
|
||||
*/
|
||||
post: function(url, data, callback, errorCallback) {
|
||||
client.post(url, data)
|
||||
.then(function(response) {
|
||||
const resp = response.result
|
||||
const code = resp.code
|
||||
// 成功,网关正常且业务正常
|
||||
if (code === '10000' && !resp.sub_code) {
|
||||
callback(resp)
|
||||
} else {
|
||||
// 报错
|
||||
Message({
|
||||
message: resp.msg,
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log('err' + error) // for debug
|
||||
errorCallback && errorCallback(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default RequestUtil
|
||||
```
|
||||
|
||||
jQuery版本如下:
|
||||
|
||||
```js
|
||||
var RequestUtil = {
|
||||
/**
|
||||
* 请求接口
|
||||
* @param url 请求路径,如http://localhost:8081/rest/food/getFoodById
|
||||
* @param data 请求数据,json格式
|
||||
* @param callback 成功回调
|
||||
* @param errorCallback 失败回调
|
||||
*/
|
||||
post: function(url, data, callback, errorCallback) {
|
||||
$.ajax({
|
||||
url: 'http://localhost:8081/api' // 网关url
|
||||
, type: 'post'
|
||||
, headers: { 'Content-Type': 'application/json' }
|
||||
, data: data
|
||||
,success:function(response) {
|
||||
var resp = response.result
|
||||
var code = resp.code
|
||||
// 成功,网关正常且业务正常
|
||||
if (code === '10000' && !resp.sub_code) {
|
||||
callback(resp)
|
||||
} else {
|
||||
// 报错
|
||||
alert(resp.msg);
|
||||
}
|
||||
}
|
||||
, error: function(error) {
|
||||
errorCallback && errorCallback(error)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
jQuery调用示例:
|
||||
|
||||
```js
|
||||
$(function () {
|
||||
var data = {
|
||||
id: 1
|
||||
,name: '葫芦娃'
|
||||
}
|
||||
RequestUtil.post('http://localhost:8081/rest/food/getFoodById', data, function (result) {
|
||||
console.log(result)
|
||||
});
|
||||
})
|
||||
```
|
||||
@@ -1,109 +0,0 @@
|
||||
# 文件上传
|
||||
|
||||
请求接口时带上文件
|
||||
|
||||
## 客户端调用
|
||||
|
||||
```java
|
||||
DemoFileUploadRequest request = new DemoFileUploadRequest();
|
||||
|
||||
DemoFileUploadModel model = new DemoFileUploadModel();
|
||||
model.setRemark("上传文件参数");
|
||||
request.setBizModel(model);
|
||||
|
||||
List<UploadFile> files = new ArrayList<>();
|
||||
String root = System.getProperty("user.dir");
|
||||
System.out.println(root);
|
||||
// 这里演示将resources下的两个文件上传到服务器
|
||||
files.add(new UploadFile("file1", new File(root + "/src/main/resources/file1.txt")));
|
||||
files.add(new UploadFile("file2", new File(root + "/src/main/resources/file2.txt")));
|
||||
request.setFiles(files);
|
||||
|
||||
DemoFileUploadResponse response = client.execute(request);
|
||||
|
||||
System.out.println("--------------------");
|
||||
if (response.isSuccess()) {
|
||||
List<DemoFileUploadResponse.FileMeta> responseFiles = response.getFiles();
|
||||
System.out.println("您上传的文件信息:");
|
||||
responseFiles.stream().forEach(file->{
|
||||
System.out.println(file);
|
||||
});
|
||||
} else {
|
||||
System.out.println("errorCode:" + response.getCode() + ",errorMsg:" + response.getMsg());
|
||||
}
|
||||
System.out.println("--------------------");
|
||||
```
|
||||
|
||||
客户端使用`UploadFile`添加上传文件
|
||||
|
||||
```java
|
||||
/**
|
||||
* @param name 表单名称,不能重复
|
||||
* @param file 文件
|
||||
* @throws IOException
|
||||
*/
|
||||
public UploadFile(String name, File file) throws IOException {
|
||||
this(name, file.getName(), FileUtil.toBytes(file));
|
||||
}
|
||||
```
|
||||
|
||||
其中`name`表示字段名称,相当于`<input type="file" name="file1"/>`中的name
|
||||
|
||||
源码详见sop-sdk
|
||||
|
||||
## 服务端接收文件
|
||||
|
||||
- 方式1
|
||||
|
||||
```java
|
||||
/**
|
||||
* 方式1:将文件写在参数中,可直接获取。好处是可以校验是否上传
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@ApiMapping(value = "demo.file.upload")
|
||||
public FileUploadVO file1(FileUploadParam param) {
|
||||
System.out.println(param.getRemark());
|
||||
// 获取上传的文件
|
||||
MultipartFile file1 = param.getFile1();
|
||||
MultipartFile file2 = param.getFile2();
|
||||
...
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Data
|
||||
public class FileUploadParam {
|
||||
private String remark;
|
||||
|
||||
// 上传文件,字段名称对应表单中的name属性值
|
||||
@NotNull(message = "文件1不能为空")
|
||||
private MultipartFile file1;
|
||||
|
||||
@NotNull(message = "文件2不能为空")
|
||||
private MultipartFile file2;
|
||||
}
|
||||
```
|
||||
|
||||
方式1的好处是可以使用`JSR-303`校验文件是否上传
|
||||
|
||||
- 方式2
|
||||
|
||||
```java
|
||||
/**
|
||||
* 方式2:从request中获取上传文件
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@ApiMapping(value = "demo.file.upload2")
|
||||
public FileUploadVO file2(FileUploadParam2 param, HttpServletRequest request) {
|
||||
System.out.println(param.getRemark());
|
||||
FileUploadVO vo = new FileUploadVO();
|
||||
// 获取上传的文件
|
||||
Collection<MultipartFile> uploadFiles = UploadUtil.getUploadFiles(request);
|
||||
...
|
||||
return vo;
|
||||
}
|
||||
```
|
||||
|
||||
微服务端源码参考`FileUploadDemoController.java`
|
||||
@@ -1,90 +0,0 @@
|
||||
# nacos注册中心
|
||||
|
||||
使用nacos作为注册中心,源码在`registry-nacos`分支
|
||||
|
||||
这里演示如何将默认的eureka注册中心替换成nacos,步骤如下:
|
||||
|
||||
- 准备工作
|
||||
|
||||
1.安装nacos,前往[最新稳定版本](https://github.com/alibaba/nacos/releases),下载最新版nacos
|
||||
|
||||
2.启动nacos服务器,cd nacos/bin
|
||||
|
||||
Linux/Unix/Mac,启动命令(standalone代表着单机模式运行,非集群模式):
|
||||
|
||||
`sh startup.sh -m standalone`
|
||||
|
||||
Windows,启动命令:
|
||||
|
||||
`cmd startup.cmd`
|
||||
|
||||
或者双击startup.cmd运行文件。
|
||||
|
||||
更多访问:https://nacos.io/zh-cn/docs/quick-start.html
|
||||
|
||||
- 微服务端修改
|
||||
|
||||
1.修改微服务应用pom,打开`sop-example/sop-story/sop-story-web/pom.xml`,注释eureka服务发现依赖,添加nacos服务发现依赖
|
||||
|
||||
```xml
|
||||
<!-- 注册中心【只能用一个,不用的注释掉】 -->
|
||||
<!-- 使用eureka注册中心
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 使用nacos注册中心
|
||||
版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。
|
||||
https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
<version>0.2.2.RELEASE</version>
|
||||
</dependency>
|
||||
<!-- 注册中心end -->
|
||||
```
|
||||
|
||||
2.yml文件新增nacos配置,并注释掉eureka相关配置
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
cloud:
|
||||
# nacos注册中心,和eureka只能用一个
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
```
|
||||
|
||||
- 网关修改
|
||||
|
||||
找到`sop-gateway`工程,步骤同上
|
||||
|
||||
- SOP-admin修改
|
||||
|
||||
|
||||
修改yml文件,设置nacos服务器地址,`registry.name`填nacos
|
||||
|
||||
```yaml
|
||||
# 注册中心地址,根据实际情况改,这里只是参数,并不会去注册
|
||||
registry:
|
||||
eureka-server-addr: http://localhost:1111/eureka/
|
||||
# nacos服务器地址
|
||||
nacos-server-addr: 127.0.0.1:8848
|
||||
# 使用eureka,填:eureka,使用nacos填:nacos
|
||||
name: nacos
|
||||
```
|
||||
|
||||
- website-server修改
|
||||
|
||||
步骤同`SOP-admin修改`
|
||||
|
||||
如果要改成consul注册中心,可参照以上步骤。
|
||||
|
||||
- 参考资料
|
||||
|
||||
1.[nacos介绍及安装](https://nacos.io/zh-cn/docs/quick-start.html)
|
||||
|
||||
2.[nacos spring cloud注册发现](https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html)
|
||||
@@ -1,112 +0,0 @@
|
||||
# 扩展其它注册中心
|
||||
|
||||
**注: nacos注册中心已经实现,本篇以nacos为例介绍如何扩展,如果要改成consul,可按照此方式进行修改**
|
||||
|
||||
SOP默认使用eureka注册中心,如果要换成nacos注册中心,步骤如下:
|
||||
|
||||
- 实现`com.gitee.sop.registryapi.service.RegistryService`接口
|
||||
|
||||
1.找到`SOP/sop-common/sop-registry-api`工程,在service.impl包下新建一个类,实现RegistryService接口
|
||||
|
||||
```java
|
||||
public class RegistryServiceNacos implements RegistryService {
|
||||
|
||||
@Override
|
||||
public List<ServiceInfo> listAllService(int pageNo, int pageSize) throws Exception {
|
||||
// TODO: 返回服务实例
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onlineInstance(ServiceInstance serviceInstance) throws Exception {
|
||||
// TODO: 实例上线
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offlineInstance(ServiceInstance serviceInstance) throws Exception {
|
||||
// TODO: 实例下线
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2.在`com.gitee.sop.registryapi.config.BaseRegistryConfig`中新增
|
||||
|
||||
```java
|
||||
/**
|
||||
* 当配置了registry.name=nacos生效
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "registry", name = "name", havingValue = "nacos")
|
||||
RegistryService registryServiceNacos() {
|
||||
return new RegistryServiceNacos();
|
||||
}
|
||||
```
|
||||
|
||||
其中`@ConditionalOnProperty(prefix = "registry", name = "name", havingValue = "nacos")`
|
||||
表示`application.properties`配置了`registry.name=nacos`参数才能生效,registry.name=nacos下文会讲到。
|
||||
|
||||
|
||||
- 微服务端修改
|
||||
|
||||
1.修改微服务应用pom.xml,注释eureka服务发现依赖,添加nacos服务发现依赖
|
||||
|
||||
```xml
|
||||
<!-- 注册中心【只能用一个,不用的注释掉】 -->
|
||||
<!-- 使用eureka注册中心
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 使用nacos注册中心
|
||||
版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。
|
||||
https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
<version>0.2.2.RELEASE</version>
|
||||
</dependency>
|
||||
<!-- 注册中心end -->
|
||||
```
|
||||
|
||||
2.yml文件新增nacos配置,并注释掉eureka相关配置
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
cloud:
|
||||
# nacos注册中心,和eureka只能用一个
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
```
|
||||
|
||||
- 网关修改
|
||||
|
||||
找到`sop-gateway`工程,步骤同上
|
||||
|
||||
- SOP-admin修改
|
||||
|
||||
|
||||
修改yml文件,新增nacos服务器地址,`registry.name`填nacos
|
||||
|
||||
```yaml
|
||||
# 注册中心地址,根据实际情况改,这里只是参数,并不会去注册
|
||||
registry:
|
||||
# 使用eureka,填:eureka,使用nacos填:nacos
|
||||
name: nacos
|
||||
eureka-server-addr: http://localhost:1111/eureka/
|
||||
nacos-server-addr: 127.0.0.1:8848
|
||||
```
|
||||
|
||||
- website-server修改
|
||||
|
||||
步骤同`SOP-admin修改`
|
||||
|
||||
- 参考资料
|
||||
|
||||
1.[nacos介绍及安装](https://nacos.io/zh-cn/docs/quick-start.html)
|
||||
|
||||
2.[nacos spring cloud注册发现](https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html)
|
||||
@@ -1,104 +0,0 @@
|
||||
# 配置Sleuth链路追踪
|
||||
|
||||
配置了Sleuth可以很方便查看微服务的调用路线图,可快速定位问题。
|
||||
|
||||
SOP基于SpringCloud,因此只要整合[Spring Cloud Sleuth](https://spring.io/projects/spring-cloud-sleuth)即可。
|
||||
除此之外,还需要支持dubbo的链路的跟踪,Sleuth在2.0已经对dubbo做了支持,详见:[brave-instrumentation-dubbo-rpc](https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo-rpc)
|
||||
|
||||
接入Spring Cloud Sleuth步骤如下:
|
||||
|
||||
- 下载zipkin服务器
|
||||
|
||||
以mac环境为例,执行下面命令,下载jar并启动zipkin服务
|
||||
|
||||
```
|
||||
curl -sSL https://zipkin.io/quickstart.sh | bash -s
|
||||
java -jar zipkin.jar
|
||||
```
|
||||
|
||||
默认端口是9411,更多安装方式详见:[quickstart](https://zipkin.io/pages/quickstart.html)
|
||||
|
||||
- sop-gateway/pom.xml添加依赖
|
||||
|
||||
```xml
|
||||
<!--开启zipkin服务链路跟踪-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
配置文件新增
|
||||
|
||||
```properties
|
||||
# zipkin服务跟踪
|
||||
spring.zipkin.base-url=http://127.0.0.1:9411/
|
||||
# 设置sleuth收集信息的比率,默认0.1,最大是1,数字越大越耗性能
|
||||
spring.sleuth.sampler.probability=1
|
||||
```
|
||||
重启sop-gateway
|
||||
|
||||
- 打开sop-story-web/pom.xml
|
||||
|
||||
添加依赖:
|
||||
|
||||
```xml
|
||||
<!--开启zipkin服务链路跟踪-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
||||
</dependency>
|
||||
<!-- zipkin支持dubbo -->
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-instrumentation-dubbo-rpc</artifactId>
|
||||
<version>5.6.6</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
配置文件新增:
|
||||
|
||||
```properties
|
||||
# zipkin服务跟踪
|
||||
spring.zipkin.base-url=http://127.0.0.1:9411/
|
||||
# 设置sleuth收集信息的比率,默认0.1,最大是1,数字越大越耗性能
|
||||
spring.sleuth.sampler.probability=1
|
||||
# dubbo使用zipkin过滤器
|
||||
dubbo.provider.filter=tracing
|
||||
dubbo.consumer.filter=tracing
|
||||
```
|
||||
|
||||
重启服务
|
||||
|
||||
- 打开sop-book/sop-book-web/pom.xml
|
||||
|
||||
步骤同上
|
||||
|
||||
- 运行DubboDemoTest.java单元测试
|
||||
|
||||
运行完毕看控制台,找到日志信息
|
||||
|
||||
```text
|
||||
2019-07-18 16:22:04.438 INFO [story-service,59dae98250b276bd,60828035658f175f,true] 90553 --- [:12345-thread-2] c.g.s.s.service.DefaultDemoService : dubbo provider, param: DemoParam(id=222)
|
||||
```
|
||||
|
||||
日志内容多了`[story-service,59dae98250b276bd,60828035658f175f,true]`部分,这些是zipkin加进去的,说明如下:
|
||||
|
||||
```text
|
||||
story-service:服务名称
|
||||
59dae98250b276bd:traceId
|
||||
60828035658f175f:spanId
|
||||
true:是否上传到zipkin服务器
|
||||
```
|
||||
|
||||
查看各个服务的控制台,可以发现traceId是一致的。
|
||||
|
||||
- 浏览器打开:http://127.0.0.1:9411/
|
||||
|
||||
将traceId复制黏贴到右上角文本框进行查询,可看到服务调用链。
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
# 预发布灰度发布
|
||||
|
||||
从1.14.0开始支持预发布、灰度发布,可登陆`SOP-Admin`,然后选择`服务列表`进行操作。
|
||||
|
||||
## 使用预发布
|
||||
|
||||
假设网关工程在阿里云负载均衡有两台服务器,域名分别为:
|
||||
|
||||
|域名|说明|
|
||||
|:---- |:---- |
|
||||
|open1.domain.com |网关服务器1 |
|
||||
|openpre.domain.com | 网关服务器2,作为预发布请求入口|
|
||||
|
||||
线上网关入口为`http://open.domain.com/api`,请求网关`http://open.domain.com/api`会负载均衡到这两台服务器
|
||||
|
||||
SOP开启预发布步骤如下:
|
||||
|
||||
修改网关工程配置文件,指定预发布域名
|
||||
|
||||
```properties
|
||||
# 预发布网关域名
|
||||
pre.domain=openpre.domain.com
|
||||
```
|
||||
重启网关
|
||||
|
||||
微服务启动参数添加:`--eureka.instance.metadata-map.env=pre`。
|
||||
建议线上配两套启动脚本,其中预发布启动脚本添加启动参数`--eureka.instance.metadata-map.env=pre`
|
||||
|
||||
登录SOP-Admin,在服务列表中点击预发布,然后预发布请求地址变成:`http://openpre.domain.com/api`。
|
||||
从`openpre.domain.com`请求进来的用户都会进预发布服务器,其它情况都走非预发布服务器。
|
||||
|
||||
## 使用灰度发布
|
||||
|
||||
灰度发布可允许指定的用户访问灰度服务器,其它用户还是走正常流程。
|
||||
|
||||
微服务启动参数添加:`--eureka.instance.metadata-map.env=gray`。
|
||||
|
||||
登录SOP-Admin,前往服务列表。
|
||||
|
||||
- 先设置灰度参数,指定灰度用户和灰度接口
|
||||
- 服务器实例开启灰度
|
||||
|
||||
参考类:
|
||||
|
||||
- PreVersionDecisionFilter.java
|
||||
- EnvironmentServerChooser.java
|
||||
@@ -1,33 +0,0 @@
|
||||
# 动态修改请求参数
|
||||
|
||||
自1.14.0开始,zuul网关支持动态修改请求参数。即在网关修改客户端传递过来的参数,然后发送到微服务端。
|
||||
|
||||
```
|
||||
客户端参数{"name": "jim"} --> zuul中修改为{"name": "Lucy"} --> 微服务端将收到{"name": "Lucy"}
|
||||
```
|
||||
|
||||
使用场景:客户端请求参数经过加密,在网关解密后,再次发送明文参数给微服务端
|
||||
|
||||
- 如何使用
|
||||
|
||||
在网关springboot启动函数中添加如下代码
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
ApiConfig.getInstance().setParameterFormatter(requestParams -> {
|
||||
// 获取biz_content
|
||||
JSONObject jsonObject = requestParams.getJSONObject(ParamNames.BIZ_CONTENT_NAME);
|
||||
// 修改biz_content中的值
|
||||
jsonObject.put("name", "name修改了111");
|
||||
jsonObject.put("remark", "remark修改了222");
|
||||
// 重新设置biz_content
|
||||
requestParams.put(ParamNames.BIZ_CONTENT_NAME, jsonObject);
|
||||
});
|
||||
SpringApplication.run(SopGatewayApplication.class, args);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
其中requestParams是客户端传递过来的参数,直接修改其中的值即可。
|
||||
|
||||
更多参考:com.gitee.sop.gatewaycommon.zuul.filter.PreParameterFormatterFilter.java
|
||||
@@ -1,21 +0,0 @@
|
||||
# 原理分析之@ApiMapping注解
|
||||
|
||||
@ApiMapping注解的使用方式参考了Spring自带的@PostMapping注解。
|
||||
|
||||
查看org.springframework.web.bind.annotation.PostMapping的类注释,有这么一句话:
|
||||
|
||||
|
||||
> Specifically, @PostMapping is a composed annotation that acts as a shortcut for @RequestMapping(method = RequestMethod.POST).
|
||||
|
||||
翻译过来就是说,@PostMapping是一个组合模式的注解,可以看成是@RequestMapping(method = RequestMethod.POST)快捷方式。
|
||||
|
||||
如果我们自己定义个Mapping,仿照@PostMapping的方式,然后作用在方法上面会不会成功呢?实践证明是可以的。
|
||||
|
||||
@ApiMapping注解正是仿照了@PostMapping注解,然后再添加了几个自己的属性,比如版本号字段。
|
||||
|
||||
那么如何才能通过path + 版本号来确定一个接口呢?
|
||||
|
||||
springmvc提供了RequestCondition接口来实现这个功能,具体的操作可参考这篇文章:[让SpringMVC支持可版本管理的Restful接口](http://www.cnblogs.com/jcli/p/springmvc_restful_version.html)
|
||||
|
||||
SOP对应的是`com.gitee.sop.servercommon.mapping.ApiMappingRequestCondition`,这个类在com.gitee.sop.servercommon.mapping下。可以从`ApiMappingHandlerMapping`类开始解读。
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# 原理分析之路由存储
|
||||
|
||||
SOP将路由信息存到了zookeeper当中,服务在启动时,将自己的路由信息上传到zookeeper中。
|
||||
网关监听存放路由的节点,动态更新到本地。
|
||||
|
||||
zookeeper存储路由的结构如下:
|
||||
|
||||
```xml
|
||||
/com.gitee.sop.route 根节点
|
||||
/serviceId 服务节点,名字为服务名
|
||||
/route1 路由节点,名字为:name+version,存放路由信息
|
||||
/route2
|
||||
/...
|
||||
```
|
||||
|
||||
服务启动时,创建`/serviceId`节点,然后遍历创建`/routeN`节点
|
||||
|
||||
同时,网关监听`服务节点`和`路由节点`,当有新服务加入时,网关会获取到新加入的路由节点信息,
|
||||
同时路由节点下面的子节点也会被监听到。后续子节点的增删改都会被网关监听到,然后更新到本地。
|
||||
|
||||
服务上传路由相关代码在`com.gitee.sop.servercommon.manager.ServiceZookeeperApiMetaManager`类中
|
||||
|
||||
网关监听相关代码在`com.gitee.sop.gatewaycommon.manager.BaseRouteManager`中
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
# 原理分析之如何路由
|
||||
|
||||
## zuul如何路由
|
||||
|
||||
SOP网关默认使用zuul,当然也默认使用了zuul提供的路由功能。zuul默认使用过滤器来实现路由转发,
|
||||
我们看下zuul中自带的过滤器:
|
||||
|
||||
| 类型 | 顺序 | 过滤器 | 功能 |
|
||||
| ----- | ---- | ----------------------- | ---------------------------- |
|
||||
| pre | -3 | ServletDetectionFilter | 标记处理 Servlet 的类型 |
|
||||
| pre | -2 | Servlet30WrapperFilter | 包装 HttpServletRequest 请求 |
|
||||
| pre | -1 | FormBodyWrapperFilter | 包装请求体 |
|
||||
| pre | 1 | DebugFilter | 标记调试标志 |
|
||||
| pre | 5 | PreDecorationFilter | 决定路由转发过滤器 |
|
||||
| route | 10 | RibbonRoutingFilter | serviceId 请求转发 |
|
||||
| route | 100 | SimpleHostRoutingFilter | url 请求转发 |
|
||||
| route | 500 | SendForwardFilter | forward 请求转发 |
|
||||
| post | 0 | SendErrorFilter | 处理有错误的请求响应 |
|
||||
| post | 1000 | SendResponseFilter | 处理正常的请求响应 |
|
||||
|
||||
上图就是zuul提供的默认过滤器,可在org.springframework.cloud.netflix.zuul.filters下查看。
|
||||
|
||||
zuul的过滤器顺序值小的优先执行,其中的`PreDecorationFilter`是我们重点关注的类,由它来决定路由转发去向。
|
||||
|
||||
打开PreDecorationFilter类,看到类注释有一句话:`that determines where and how to route based on the supplied`
|
||||
|
||||
翻译过来就是说,决定从哪里获取路由,然后怎样去路由。
|
||||
|
||||
PreDecorationFilter类的核心方法是run()方法。找到run方法中这一句代码:
|
||||
|
||||
`Route route = this.routeLocator.getMatchingRoute(requestURI);`
|
||||
|
||||
这句代码很重要,表示路由从哪里获取,如果我们能够重写getMatchingRoute方法那就可以返回自己定义的路由了。
|
||||
|
||||
接下来找到RouteLocator类的定义,发现是通过构造方法传进来的,那么我们就去找使用构造方法的类。(IDEA下右键构造方法--Find Usage)
|
||||
|
||||
在org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration类中找到了定义
|
||||
|
||||
```java
|
||||
// pre filters
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(PreDecorationFilter.class)
|
||||
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) {
|
||||
return new PreDecorationFilter(routeLocator, this.server.getServlet().getContextPath(), this.zuulProperties,
|
||||
proxyRequestHelper);
|
||||
}
|
||||
```
|
||||
|
||||
方法默认注入了RouteLocator类,默认注入的实现是CompositeRouteLocator类(通过打断点可以查看)。
|
||||
|
||||
同时方法上用了`@ConditionalOnMissingBean`注解,表示如果其它地方没有声明,则默认使用这个。
|
||||
|
||||
因此我们可以自己声明一个PreDecorationFilter,然后注入自定义的RouteLocator就行了。
|
||||
|
||||
SOP自定义的RouteLocator为:`com.gitee.sop.gatewaycommon.zuul.route.SopRouteLocator`,可自行前往查看。
|
||||
|
||||
然后再我们的Config中定义:
|
||||
|
||||
```java
|
||||
/**
|
||||
* 选取路由
|
||||
* @param zuulRouteRepository
|
||||
* @param proxyRequestHelper
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public PreDecorationFilter preDecorationFilter(ZuulRouteRepository zuulRouteRepository, ProxyRequestHelper proxyRequestHelper) {
|
||||
// 自定义路由
|
||||
RouteLocator routeLocator = new SopRouteLocator(zuulRouteRepository);
|
||||
return new PreDecorationFilter(routeLocator,
|
||||
this.server.getServlet().getContextPath(),
|
||||
this.zuulProperties,
|
||||
proxyRequestHelper);
|
||||
}
|
||||
```
|
||||
|
||||
到此,我们只需要实现RouteLocator接口,就能使用zuul默认的路由功能,非常方便。
|
||||
@@ -1,26 +0,0 @@
|
||||
# 原理分析之文档归纳
|
||||
|
||||
作为开放平台,必须要提供API文档。
|
||||
|
||||
SOP采用微服务架构实现,因此文档应该由各个微服务各自实现。难点是如何归纳各个微服务端提供的文档信息,并统一展示。
|
||||
|
||||
SOP的解决思路如下:
|
||||
|
||||
- 各微服务使用swagger定义自己的接口信息
|
||||
- sop-website项目在启动时向注册中心获取所有服务实例,分别调用各个服务提供的swagger文档信息,保存到本地
|
||||
- sop-website前端页面负责展示swagger提供的文档信息
|
||||
|
||||
由于注册中心的存在,可以很方便的获取每个微服务提供的接口,因此可以获取到swagger提供的文档信息。
|
||||
|
||||
如此一来的好处是,各微服务不用关心文档该怎么展示,只需要写好swagger注解即可;文档信息展示统一交给另外一个工程来维护,各司其职。
|
||||
|
||||
SOP设计初衷亦是如此,微服务只管写业务代码,其它的都交给SOP来处理。
|
||||
|
||||
文档归纳原理图:
|
||||
|
||||

|
||||
|
||||
- sop-website服务启动时向各微服务获取接口信息,保存到本地
|
||||
- 用户访问website页面,website提供对应的接口文档并展示
|
||||
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
# 常见问题
|
||||
|
||||
## 微服务端如何获取appId等参数
|
||||
|
||||
```java
|
||||
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
|
||||
String appId = openContext.getAppId();
|
||||
```
|
||||
|
||||
## 在其它地方获取业务参数
|
||||
|
||||
```java
|
||||
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
|
||||
Story bizObject = (Story)openContext.getBizObject();
|
||||
|
||||
或
|
||||
|
||||
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
|
||||
Story bizObject = openContext.getBizObject(Story.class);
|
||||
```
|
||||
|
||||
## Socket error occurred: `localhost/0:0:0:0:0:0:0:1:2181`: Connection refused
|
||||
|
||||
检查本地zookeeper有没启动,如果zookeeper在其他机器上,修改application-dev.yml
|
||||
|
||||
```yaml
|
||||
cloud:
|
||||
zookeeper:
|
||||
connect-string: ip:2181
|
||||
```
|
||||
|
||||
## 如何关闭签名验证
|
||||
|
||||
- 针对某一个接口关闭签名验证
|
||||
`@ApiMapping(value = "alipay.story.get", ignoreValidate = true)`
|
||||
|
||||
- 针对所有接口关闭签名验证
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class ZuulConfig extends AlipayZuulConfiguration {
|
||||
|
||||
static {
|
||||
...
|
||||
ApiConfig.getInstance().setIgnoreValidate(true);
|
||||
...
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## 注册到eureka显示hostname,非ip
|
||||
|
||||
```yaml
|
||||
eureka:
|
||||
instance:
|
||||
prefer-ip-address: true
|
||||
instance-id: ${spring.cloud.client.ip-address}:${server.port}
|
||||
```
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
参考:https://www.jianshu.com/p/5ad8317961b7
|
||||
|
||||
## 直接访问服务的swagger-ui.html ,提示access forbidden
|
||||
|
||||
找到微服务的`OpenServiceConfig.java`,重写内部类Swagger2中的swaggerAccessProtected()方法,返回false。线上请设置成true
|
||||
|
||||
```java
|
||||
// 开启文档
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public static class Swagger2 extends SwaggerSupport {
|
||||
@Override
|
||||
protected String getDocTitle() {
|
||||
return "故事API";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean swaggerAccessProtected() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 调试网关出现服务不可用
|
||||
|
||||
打断点调试,网关出现Read Timeout
|
||||
|
||||
参考:https://blog.csdn.net/qq_36872046/article/details/81058045
|
||||
|
||||
yml添加:
|
||||
|
||||
```yaml
|
||||
ribbon:
|
||||
# https://blog.csdn.net/qq_36872046/article/details/81058045
|
||||
# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。
|
||||
# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点
|
||||
ReadTimeout: 60000
|
||||
```
|
||||
|
Before Width: | Height: | Size: 206 KiB |
|
Before Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 63 KiB |
@@ -1,68 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SOP开发文档</title>
|
||||
<link rel="icon" href="_media/favicon.ico">
|
||||
<meta name="google-site-verification" content="6t0LoIeFksrjF4c9sqUEsVXiQNxLp2hgoqo0KryT-sE" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="keywords" content="doc,docs,documentation,gitbook,creator,generator,github,jekyll,github-pages">
|
||||
<meta name="description" content="A magical documentation generator.">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css" title="vue" >
|
||||
<style>
|
||||
nav.app-nav li ul {
|
||||
min-width: 100px;
|
||||
}
|
||||
/* 显示区域宽度 */
|
||||
.markdown-section{
|
||||
max-width: 90%;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">加载中 ...</div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
alias: {
|
||||
'/.*/_sidebar.md': '/_sidebar.md?t=' + new Date().getTime()
|
||||
},
|
||||
auto2top: true,
|
||||
coverpage: false,
|
||||
executeScript: true,
|
||||
loadSidebar: true,
|
||||
loadNavbar: false,
|
||||
mergeNavbar: true,
|
||||
maxLevel: 3,
|
||||
subMaxLevel: 2,
|
||||
ga: 'UA-106147152-1',
|
||||
name: '',
|
||||
search: {
|
||||
noData: {
|
||||
'/zh-cn/': '没有结果!',
|
||||
'/': '没有结果!'
|
||||
},
|
||||
paths: 'auto',
|
||||
placeholder: {
|
||||
'/zh-cn/': '搜索',
|
||||
'/': '搜索'
|
||||
}
|
||||
},
|
||||
formatUpdated: '{MM}/{DD} {HH}:{mm}'
|
||||
}
|
||||
</script>
|
||||
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
|
||||
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
||||
<script src="//unpkg.com/docsify/lib/plugins/ga.min.js"></script>
|
||||
<script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
|
||||
<script src="//unpkg.com/prismjs/components/prism-markdown.min.js"></script>
|
||||
<script src="//unpkg.com/prismjs/components/prism-java.min.js"></script>
|
||||
<script src="//unpkg.com/prismjs/components/prism-json.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
49
doc/pom.xml
@@ -1,49 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>doc</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<!-- Generic properties -->
|
||||
<java.version>1.8</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- 打包时跳过测试 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.18.1</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -1 +0,0 @@
|
||||
docsify serve docs
|
||||
@@ -1,55 +0,0 @@
|
||||
const liveServer = require('live-server')
|
||||
const isSSR = !!process.env.SSR
|
||||
const middleware = []
|
||||
|
||||
if (isSSR) {
|
||||
const Renderer = require('./packages/docsify-server-renderer/build.js')
|
||||
const renderer = new Renderer({
|
||||
template: `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>docsify</title>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="stylesheet" href="/themes/vue.css" title="vue">
|
||||
</head>
|
||||
<body>
|
||||
<!--inject-app-->
|
||||
<!--inject-config-->
|
||||
<script src="/lib/docsify.js"></script>
|
||||
</body>
|
||||
</html>`,
|
||||
config: {
|
||||
name: 'docsify',
|
||||
repo: 'docsifyjs/docsify',
|
||||
basePath: 'https://docsify.js.org/',
|
||||
loadNavbar: true,
|
||||
loadSidebar: true,
|
||||
subMaxLevel: 3,
|
||||
auto2top: true,
|
||||
alias: {
|
||||
'/de-de/changelog': '/changelog',
|
||||
'/zh-cn/changelog': '/changelog',
|
||||
'/changelog':
|
||||
'https://raw.githubusercontent.com/docsifyjs/docsify/master/CHANGELOG'
|
||||
}
|
||||
},
|
||||
path: './'
|
||||
})
|
||||
|
||||
middleware.push(function(req, res, next) {
|
||||
if (/\.(css|js)$/.test(req.url)) {
|
||||
return next()
|
||||
}
|
||||
renderer.renderToString(req.url).then(html => res.end(html))
|
||||
})
|
||||
}
|
||||
|
||||
const params = {
|
||||
port: 3000,
|
||||
watch: ['lib', 'docs', 'themes'],
|
||||
middleware
|
||||
}
|
||||
|
||||
liveServer.start(params)
|
||||
@@ -1,96 +0,0 @@
|
||||
package com.gitee.sop.doc;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 生成_sidebar.md文件,直接运行即可
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public class SidebarTest {
|
||||
|
||||
static String format = " * [%s](files/%s?t=%s)\r\n";
|
||||
static Map<String, Menu> levelMap = new HashMap<>(8);
|
||||
|
||||
static {
|
||||
int i = 0;
|
||||
levelMap.put("1", new Menu("* 开发文档\n", i++));
|
||||
levelMap.put("9", new Menu("* 原理分析\n", i++));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String path = SidebarTest.class.getClassLoader().getResource("").getPath();
|
||||
String root = path.substring(0, path.indexOf("doc")) + "doc";
|
||||
String fileDir = root + "/docs/files";
|
||||
File dir = new File(fileDir);
|
||||
File[] files = dir.listFiles();
|
||||
Stream<File> filesStream = Stream.of(files);
|
||||
Map<String, List<FileExt>> menuMap = filesStream
|
||||
.sorted(Comparator.comparing(File::getName))
|
||||
.map(file -> {
|
||||
if (file.isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
FileExt fileExt = new FileExt();
|
||||
fileExt.menu = file.getName().substring(0, 1);
|
||||
fileExt.file = file;
|
||||
return fileExt;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.groupingBy(FileExt::getMenu));
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
output.append("* [首页](/?t=" + System.currentTimeMillis() + ")\n");
|
||||
for (Map.Entry<String, List<FileExt>> entry : menuMap.entrySet()) {
|
||||
Menu menu = levelMap.get(entry.getKey());
|
||||
output.append(menu.parentName);
|
||||
for (FileExt fileExt : entry.getValue()) {
|
||||
String filename = fileExt.file.getName();
|
||||
String title = filename.substring(filename.indexOf("_") + 1, filename.length() - 3);
|
||||
String line = String.format(format, title, filename, System.currentTimeMillis());
|
||||
output.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(output);
|
||||
|
||||
String sidebarFilepath = root + "/docs/_sidebar.md";
|
||||
|
||||
FileOutputStream out = new FileOutputStream(new File(sidebarFilepath));
|
||||
out.write(output.toString().getBytes());
|
||||
out.close();
|
||||
}
|
||||
|
||||
static class Menu {
|
||||
String parentName;
|
||||
int order;
|
||||
|
||||
public Menu(String parentName, int order) {
|
||||
this.parentName = parentName;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class FileExt {
|
||||
File file;
|
||||
String menu;
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public String getMenu() {
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
docker-build.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 打包
|
||||
mvn clean package
|
||||
# 创建镜像
|
||||
docker build -t gitee.com/sop .
|
||||
|
||||
# 获取镜像id
|
||||
image_id=`docker images gitee.com/sop --format "{{.ID}}" | awk '{print $1}'`
|
||||
|
||||
# 运行镜像
|
||||
docker run --name sop -p 8081:8081 -p 8082:8082 -p 8083:8083 -p 8087:8087 -p 2222:2222 -d $image_id
|
||||
11
docker-entrypoint.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
JAVA_OPTS="-Xms128m -Xmx128m"
|
||||
|
||||
# mysql, nacos配置
|
||||
args="--mysql.host=10.1.30.110:3306 --mysql.username=root --mysql.password=root --dubbo.registry.address=10.1.30.110:8848"
|
||||
|
||||
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-gateway/sop-gateway.jar $args --logging.file.path=/sop/sop-gateway/log &
|
||||
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-admin/sop-admin.jar $args --logging.file.path=/sop/sop-admin/log &
|
||||
# 最后一条没有&
|
||||
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-story/sop-story.jar $args --logging.file.path=/sop/sop-story/log
|
||||
233
pom.xml
Normal file → Executable file
@@ -2,23 +2,238 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.6.15</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-parent</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<description>一个开放平台解决方案项目,基于Spring Cloud实现,目标是能够让用户快速得搭建起自己的开放平台</description>
|
||||
<description>一个开放平台解决方案项目,基于Dubbo实现,目标是能够让用户快速得搭建起自己的开放平台</description>
|
||||
|
||||
<modules>
|
||||
<module>doc</module>
|
||||
<module>sop-common</module>
|
||||
<module>sop-example</module>
|
||||
<module>sop-admin</module>
|
||||
<module>sop-registry</module>
|
||||
<module>sop-gateway</module>
|
||||
<module>sop-website</module>
|
||||
<module>sop-test</module>
|
||||
<module>sop-sdk</module>
|
||||
<module>sop-website</module>
|
||||
<module>sop-gateway</module>
|
||||
<module>sop-registry</module>
|
||||
<module>sop-support</module>
|
||||
<module>sop-notify</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<!-- springboot 版本-->
|
||||
<spring-boot.version>2.6.15</spring-boot.version>
|
||||
<!-- spring cloud 版本 -->
|
||||
<spring-cloud.version>2021.0.5</spring-cloud.version>
|
||||
<!-- spring cloud alibaba 版本 -->
|
||||
<!-- 具体版本对应关系见:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E -->
|
||||
<spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
|
||||
<!-- dubbo版本 -->
|
||||
<dubbo.version>3.2.16</dubbo.version>
|
||||
|
||||
<junit.version>4.11</junit.version>
|
||||
<commons-io.version>2.5</commons-io.version>
|
||||
<commons-fileupload.version>1.3.3</commons-fileupload.version>
|
||||
<commons-collection.version>3.2.2</commons-collection.version>
|
||||
<commons-lang3.version>3.8.1</commons-lang3.version>
|
||||
<commons-codec.version>1.11</commons-codec.version>
|
||||
<commons-logging.version>1.2</commons-logging.version>
|
||||
<validation-api.version>2.0.1.Final</validation-api.version>
|
||||
<hibernate-validator.version>6.0.13.Final</hibernate-validator.version>
|
||||
<fastmybatis.version>3.1.7</fastmybatis.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- 加了这个就不需要加版本号了 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
|
||||
<version>${spring-cloud-alibaba.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-bom</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.gitee.durcframework</groupId>
|
||||
<artifactId>fastmybatis-spring-boot-starter</artifactId>
|
||||
<version>${fastmybatis.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.oschina.durcframework</groupId>
|
||||
<artifactId>http-helper</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.52</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.14.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>${validation-api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>${hibernate-validator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
<version>2.14.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>32.1.3-jre</version>
|
||||
</dependency>
|
||||
|
||||
<!-- commons -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-collections</groupId>
|
||||
<artifactId>commons-collections</artifactId>
|
||||
<version>${commons-collection.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons-io.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>${commons-codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>${commons-fileupload.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>${commons-logging.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.34</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>aliyun</id>
|
||||
<name>aliyun</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- 打包时跳过测试 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.12.4</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<configuration>
|
||||
<configLocation>checkstyle.xml</configLocation>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>true</failsOnError>
|
||||
<linkXRef>false</linkXRef>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>validate</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
21
script/startup.sh
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 执行文件名称
|
||||
app_name=${1}
|
||||
|
||||
if [ -z "${1}" ];then
|
||||
echo "未指定应用程序参数,格式:sh startup.sh xxx.jar"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "启动程序:${app_name}"
|
||||
|
||||
# 先关闭服务
|
||||
pid=$(ps -ef | grep $app_name | grep -v grep | grep -v .sh | awk '{print $2}')
|
||||
if [ -n "$pid" ]; then
|
||||
echo "stop $app_name, pid:$pid"
|
||||
kill -9 "$pid"
|
||||
fi
|
||||
|
||||
|
||||
nohup java -server -jar -Dspring.profiles.active=test -Duser.timezone=Asia/Shanghai -Xms512m -Xmx512m $app_name >/dev/null 2>&1 &
|
||||
@@ -1,13 +0,0 @@
|
||||
DROP TABLE IF EXISTS `config_common`;
|
||||
|
||||
CREATE TABLE `config_common` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`config_group` varchar(64) NOT NULL DEFAULT '' COMMENT '配置分组',
|
||||
`config_key` varchar(64) NOT NULL DEFAULT '' COMMENT '配置key',
|
||||
`content` varchar(128) NOT NULL DEFAULT '' COMMENT '内容',
|
||||
`remark` varchar(128) DEFAULT NULL COMMENT '备注',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_groupkey` (`config_group`,`config_key`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='通用配置表';
|
||||
@@ -1,20 +0,0 @@
|
||||
DROP TABLE IF EXISTS `isv_keys`;
|
||||
|
||||
CREATE TABLE `isv_keys` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`app_key` varchar(128) NOT NULL DEFAULT '应用id(交给开发者)',
|
||||
`sign_type` tinyint(4) NOT NULL DEFAULT '1' COMMENT '1:RSA2,2:MD5',
|
||||
`secret` varchar(200) NOT NULL DEFAULT '' COMMENT 'sign_type=2时使用',
|
||||
`key_format` tinyint(4) NOT NULL DEFAULT '1' COMMENT '秘钥格式,1:PKCS8(JAVA适用),2:PKCS1(非JAVA适用)',
|
||||
`public_key_isv` text NOT NULL COMMENT '开发者生成的公钥',
|
||||
`private_key_isv` text NOT NULL COMMENT '开发者生成的私钥(交给开发者)',
|
||||
`public_key_platform` text NOT NULL COMMENT '平台生成的公钥(交给开发者)',
|
||||
`private_key_platform` text NOT NULL COMMENT '平台生成的私钥',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_appkey` (`app_key`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ISV秘钥';
|
||||
|
||||
INSERT INTO `sop`.`isv_keys` (`app_key`,`sign_type`, `secret`,`key_format`, `public_key_isv`, `private_key_isv`, `public_key_platform`, `private_key_platform`)
|
||||
select app_key, sign_type, secret, 1, pub_key,pri_key,'','' from isv_info;
|
||||
@@ -1,2 +0,0 @@
|
||||
ALTER TABLE `sop`.`isv_info` ADD COLUMN `remark` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注' AFTER `sign_type`;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
use sop;
|
||||
|
||||
CREATE TABLE `config_ip_blacklist` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`ip` varchar(64) NOT NULL DEFAULT '' COMMENT 'ip',
|
||||
`remark` varchar(128) DEFAULT NULL COMMENT '备注',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_ip` (`ip`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='IP黑名单';
|
||||
@@ -1,25 +0,0 @@
|
||||
use sop;
|
||||
|
||||
CREATE TABLE `config_gray` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`service_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`user_key_content` text COMMENT '用户key,多个用引文逗号隔开',
|
||||
`name_version_content` text COMMENT '需要灰度的接口,goods.get1.0=1.2,多个用英文逗号隔开',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_serviceid` (`service_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='服务灰度配置';
|
||||
|
||||
|
||||
CREATE TABLE `config_gray_instance` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`instance_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'instance_id',
|
||||
`service_id` varchar(64) NOT NULL DEFAULT '' COMMENT 'service_id',
|
||||
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:禁用,1:启用',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_instanceid` (`instance_id`) USING BTREE,
|
||||
KEY `idx_serviceid` (`service_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='开启灰度服务器实例';
|
||||
@@ -1,3 +0,0 @@
|
||||
ALTER TABLE `sop`.`isv_info` ADD COLUMN `sign_type` TINYINT NOT NULL DEFAULT 1 COMMENT '签名类型,1:RSA2,2:MD5' AFTER `status`;
|
||||
|
||||
update isv_info set sign_type=2 where secret <> '';
|
||||
@@ -1,16 +0,0 @@
|
||||
DROP TABLE IF EXISTS `user_info`;
|
||||
|
||||
CREATE TABLE `user_info` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名',
|
||||
`password` varchar(128) NOT NULL DEFAULT '' COMMENT '密码',
|
||||
`nickname` varchar(64) NOT NULL DEFAULT '' COMMENT '昵称',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_unamepwd` (`username`,`password`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息表';
|
||||
|
||||
|
||||
INSERT INTO `user_info` ( `username`, `password`, `nickname`, `gmt_create`, `gmt_modified`) VALUES
|
||||
('zhangsan','123456','张三','2019-04-27 08:32:57','2019-04-27 08:32:57');
|
||||
@@ -1,32 +0,0 @@
|
||||
|
||||
DROP TABLE IF EXISTS `config_limit`;
|
||||
|
||||
|
||||
CREATE TABLE `config_limit` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`route_id` varchar(128) DEFAULT NULL COMMENT '路由id',
|
||||
`app_key` varchar(128) DEFAULT NULL,
|
||||
`limit_ip` varchar(300) DEFAULT NULL COMMENT '限流ip,多个用英文逗号隔开',
|
||||
`service_id` varchar(64) NOT NULL DEFAULT '' COMMENT '服务id',
|
||||
`limit_type` tinyint(4) NOT NULL DEFAULT '1' COMMENT '限流策略,1:漏桶策略,2:令牌桶策略',
|
||||
`exec_count_per_second` int(11) DEFAULT NULL COMMENT '每秒可处理请求数',
|
||||
`limit_code` varchar(64) DEFAULT NULL COMMENT '返回的错误码',
|
||||
`limit_msg` varchar(100) DEFAULT NULL COMMENT '返回的错误信息',
|
||||
`token_bucket_count` int(11) DEFAULT NULL COMMENT '令牌桶容量',
|
||||
`limit_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '限流开启状态,1:开启,0关闭',
|
||||
`order_index` int(11) NOT NULL DEFAULT '0' COMMENT '顺序,值小的优先执行',
|
||||
`remark` varchar(128) DEFAULT NULL COMMENT '备注',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='限流配置';
|
||||
|
||||
INSERT INTO `config_limit` (`id`, `route_id`, `app_key`, `limit_ip`, `service_id`, `limit_type`, `exec_count_per_second`, `limit_code`, `limit_msg`, `token_bucket_count`, `limit_status`, `order_index`, `remark`, `gmt_create`, `gmt_modified`) VALUES
|
||||
(1,'alipay.story.get1.0','','192.168.1.1,172.2.2.3','story-service',2,5,'','',6,1,3,NULL,'2019-05-17 19:21:35','2019-05-21 09:12:15'),
|
||||
(2,'alipay.story.get1.0','2019032617262200001','','story-service',1,5,'service-budy','服务器忙',5,1,0,NULL,'2019-05-17 19:39:30','2019-05-21 15:36:52'),
|
||||
(3,'alipay.story.find1.0','20190331562013861008375808','','story-service',1,3,'service-busy','服务器忙',5,1,1,NULL,'2019-05-17 20:20:32','2019-05-20 17:40:17'),
|
||||
(4,'alipay.story.get1.2','','','story-service',2,5,'','',3,1,1,NULL,'2019-05-20 16:27:21','2019-05-21 15:53:10'),
|
||||
(5,'','20190401562373796095328256','','story-service',1,5,'service-busy','服务器忙',5,1,0,'这个appKey调用很频繁,重点照顾','2019-05-21 15:48:08','2019-05-21 18:45:32'),
|
||||
(6,'','','10.1.30.54','story-service',1,5,'service-busy','服务器忙',5,1,0,'这个ip在攻击我们','2019-05-21 15:55:33','2019-05-21 18:17:29'),
|
||||
(7,'story.get1.1','','10.1.30.54','story-service',1,5,'service-busy','服务器忙',5,1,0,NULL,'2019-05-21 16:30:48','2019-05-21 16:30:48'),
|
||||
(8,'','20190513577548661718777857','10.1.30.54','story-service',1,5,'service-busy','服务器忙',5,1,0,NULL,'2019-05-21 17:10:45','2019-05-21 17:10:52');
|
||||
14
sop-admin/pom.xml
Normal file → Executable file
@@ -2,14 +2,18 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<parent>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-parent</artifactId>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>sop-admin</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>sop-admin-front</module>
|
||||
<module>sop-admin-server</module>
|
||||
<module>sop-admin-backend</module>
|
||||
<module>sop-code-gen</module>
|
||||
</modules>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
7
sop-admin/readme.md
Normal file → Executable file
@@ -1,5 +1,6 @@
|
||||
# 后台admin
|
||||
|
||||
- sop-admin-front: admin前端layui实现(停止维护,改用sop-admin-vue)
|
||||
- sop-admin-server: admin服务端
|
||||
- sop-admin-vue: admin前端vue实现
|
||||
- sop-admin-backend: admin服务端
|
||||
- sop-admin-frontend: admin前端实现
|
||||
- sop-code-gen: 代码生成器
|
||||
|
||||
|
||||
139
sop-admin/sop-admin-backend/admin-boot/pom.xml
Executable file
@@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-admin-backend</artifactId>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>admin-boot</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>admin-web</artifactId>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- nacos注册中心 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- zookeeper注册中心 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-zookeeper-curator5-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- provided -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>aliyun</id>
|
||||
<name>aliyun</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<finalName>sop-admin-${project.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- 打包时跳过测试 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.12.4</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<configuration>
|
||||
<configLocation>checkstyle.xml</configLocation>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>true</failsOnError>
|
||||
<linkXRef>false</linkXRef>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>validate</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.gitee.sop.admin;
|
||||
|
||||
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDubbo
|
||||
public class SopAdminApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SopAdminApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.gitee.sop.admin.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "admin")
|
||||
@Data
|
||||
public class AdminConfig {
|
||||
|
||||
private int jwtTimeoutDays;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.gitee.sop.admin.config;
|
||||
|
||||
import com.gitee.sop.admin.common.context.SpringContext;
|
||||
import com.gitee.sop.admin.common.util.SystemUtil;
|
||||
import com.gitee.sop.admin.interceptor.LoginInterceptor;
|
||||
import com.gitee.sop.admin.service.sys.UserCacheService;
|
||||
import com.gitee.sop.admin.service.sys.impl.LocalUserCacheService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class SopAdminConfiguration implements ApplicationContextAware, WebMvcConfigurer {
|
||||
|
||||
@Value("${front-location:}")
|
||||
private String frontLocation;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
SpringContext.setApplicationContext(applicationContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "user.cache.type", havingValue = "local", matchIfMissing = true)
|
||||
public UserCacheService userCacheService() {
|
||||
return new LocalUserCacheService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置拦截器
|
||||
*
|
||||
* @param registry
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
String[] excludes = {
|
||||
"/", "/error",
|
||||
// 排除前端资源
|
||||
"/*.html", "/*.ico", "/*.png", "/*.json", "/static/**", "/assets/**"
|
||||
};
|
||||
registry.addInterceptor(new LoginInterceptor())
|
||||
.excludePathPatterns(excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨域设置
|
||||
*/
|
||||
@Bean
|
||||
public CorsFilter corsFilter(
|
||||
@Value("${torna.cors.allowed-origin-pattern:*}") String allowedOriginPattern,
|
||||
@Value("${torna.cors.allowed-header:*}") String allowedHeader
|
||||
) {
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
// SpringBoot升级2.4.0之后,跨域配置中的.allowedOrigins不再可用,改成addAllowedOriginPattern
|
||||
corsConfiguration.addAllowedOriginPattern(allowedOriginPattern);
|
||||
corsConfiguration.addAllowedHeader(allowedHeader);
|
||||
corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
|
||||
corsConfiguration.addExposedHeader("Content-Disposition");
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 配置静态资源
|
||||
*
|
||||
* @param registry
|
||||
*/
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
String homeDir = SystemUtil.getBinPath();
|
||||
String frontRoot;
|
||||
if (StringUtils.hasText(frontLocation)) {
|
||||
frontRoot = StringUtils.trimTrailingCharacter(frontLocation, '/');
|
||||
} else {
|
||||
frontRoot = homeDir + "/dist";
|
||||
}
|
||||
log.info("前端资源目录:{}", frontRoot);
|
||||
String location = "file:" + frontRoot;
|
||||
registry.addResourceHandler("/index.html").addResourceLocations(location + "/index.html");
|
||||
registry.addResourceHandler("/favicon.ico").addResourceLocations(location + "/favicon.ico");
|
||||
registry.addResourceHandler("/logo.png").addResourceLocations(location + "/logo.png");
|
||||
registry.addResourceHandler("/platform-config.json").addResourceLocations(location + "/platform-config.json");
|
||||
registry.addResourceHandler("/static/**").addResourceLocations(location + "/static/");
|
||||
registry.addResourceHandler("/assets/**").addResourceLocations(location + "/assets/");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gitee.sop.admin.interceptor;
|
||||
|
||||
import com.gitee.sop.admin.common.annotation.NoToken;
|
||||
import com.gitee.sop.admin.common.enums.StatusEnum;
|
||||
import com.gitee.sop.admin.common.user.User;
|
||||
import com.gitee.sop.admin.common.context.UserContext;
|
||||
import com.gitee.sop.admin.common.exception.LoginFailureException;
|
||||
import com.gitee.sop.admin.common.util.RequestUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class LoginInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
if (!(handler instanceof HandlerMethod)) {
|
||||
return true;
|
||||
}
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
NoToken noLogin = handlerMethod.getMethodAnnotation(NoToken.class);
|
||||
if (noLogin != null) {
|
||||
return true;
|
||||
}
|
||||
noLogin = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), NoToken.class);
|
||||
if (noLogin != null) {
|
||||
return true;
|
||||
}
|
||||
User user = UserContext.getUser(request);
|
||||
if (user == null || StatusEnum.of(user.getStatus()) == StatusEnum.DISABLED) {
|
||||
log.error("登录失败, 客户端ip:{}, uri:{}", RequestUtil.getIP(request), request.getRequestURI());
|
||||
throw new LoginFailureException("登录失败");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
dubbo.registry.address=zookeeper://localhost:2181
|
||||
|
||||
mybatis.print-sql=true
|
||||
|
||||
# mysql config
|
||||
mysql.host=127.0.0.1:3306
|
||||
mysql.db=sop
|
||||
mysql.username=root
|
||||
mysql.password=root
|
||||
@@ -0,0 +1,9 @@
|
||||
dubbo.registry.address=nacos://localhost:8848
|
||||
|
||||
mybatis.print-sql=true
|
||||
|
||||
# mysql config
|
||||
mysql.host=127.0.0.1:3306
|
||||
mysql.db=sop
|
||||
mysql.username=root
|
||||
mysql.password=root
|
||||
@@ -0,0 +1,52 @@
|
||||
server.port=8082
|
||||
spring.profiles.active=dev
|
||||
|
||||
spring.application.name=sop-admin
|
||||
|
||||
index.path=/
|
||||
|
||||
####### admin config #######
|
||||
# user cache
|
||||
user.cache.type=local
|
||||
|
||||
dubbo.protocol.name=dubbo
|
||||
dubbo.protocol.port=-1
|
||||
dubbo.application.qos-enable=false
|
||||
dubbo.consumer.check=false
|
||||
# ### register config see:https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/registry/overview/
|
||||
# ------
|
||||
# nacos://localhost:8848 Cluster config:nacos://localhost:8848?backup=localshot:8846,localshot:8847
|
||||
# zookeeper://localhost:2181 Cluster config:zookeeper://10.20.153.10:2181?backup=10.20.153.11:2181,10.20.153.12:2181
|
||||
# redis://localhost:6379 Cluster config:redis://10.20.153.10:6379?backup=10.20.153.11:6379,10.20.153.12:6379
|
||||
# ------
|
||||
dubbo.registry.address=zookeeper://localhost:2181
|
||||
|
||||
####### mysql config #######
|
||||
mysql.host=127.0.0.1:3306
|
||||
mysql.db=sop
|
||||
mysql.username=
|
||||
mysql.password=
|
||||
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.datasource.url=jdbc:mysql://${mysql.host}/${mysql.db}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
|
||||
spring.datasource.username=${mysql.username}
|
||||
spring.datasource.password=${mysql.password}
|
||||
|
||||
####### mybatis config #######
|
||||
mybatis.fill.com.gitee.fastmybatis.core.support.LocalDateTimeFillInsert=add_time
|
||||
mybatis.fill.com.gitee.fastmybatis.core.support.LocalDateTimeFillUpdate=update_time
|
||||
mybatis.fill.com.gitee.sop.admin.common.fill.AddByFill=
|
||||
mybatis.fill.com.gitee.sop.admin.common.fill.UpdateByFill=
|
||||
# mybatis config file
|
||||
mybatis.config-location=classpath:mybatis/mybatisConfig.xml
|
||||
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
|
||||
|
||||
# log level
|
||||
logging.level.com.gitee.sop=info
|
||||
# log path
|
||||
logging.file.name=logs/sop-admin.log
|
||||
# print SQL
|
||||
logging.level.com.gitee.sop.admin.dao=error
|
||||
logging.level.com.gitee.fastmybatis=info
|
||||
mybatis.print-sql=false
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.gitee.sop.admin;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
public class BaseTest {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.gitee.sop.admin.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.gitee.sop.admin.BaseTest;
|
||||
import com.gitee.sop.admin.service.sys.login.LoginService;
|
||||
import com.gitee.sop.admin.service.sys.login.dto.LoginDTO;
|
||||
import com.gitee.sop.admin.service.sys.login.dto.LoginUser;
|
||||
import com.gitee.sop.admin.service.sys.login.enums.RegTypeEnum;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.crypto.bcrypt.BCrypt;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class LoginServiceTest extends BaseTest {
|
||||
|
||||
@Autowired
|
||||
LoginService loginService;
|
||||
|
||||
@Test
|
||||
public void login() {
|
||||
LoginDTO loginDTO = new LoginDTO();
|
||||
loginDTO.setUsername("admin");
|
||||
loginDTO.setPassword("123456");
|
||||
loginDTO.setRegType(RegTypeEnum.BACKEND);
|
||||
LoginUser loginUser = loginService.login(loginDTO);
|
||||
Assert.notNull(loginUser, "not null");
|
||||
System.out.println(JSON.toJSONString(loginUser));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetAdminPwd() {
|
||||
// 初始密码
|
||||
String defPassword = "123456";
|
||||
defPassword = DigestUtils.sha256Hex(defPassword);
|
||||
String encodedPassword = BCrypt.hashpw(defPassword, BCrypt.gensalt());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.gitee.sop.admin.service;
|
||||
|
||||
import com.gitee.sop.admin.BaseTest;
|
||||
import com.gitee.sop.admin.dao.entity.SysUser;
|
||||
import com.gitee.sop.admin.service.sys.SysUserService;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.bcrypt.BCrypt;
|
||||
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class PasswordTest extends BaseTest {
|
||||
|
||||
@Autowired
|
||||
SysUserService sysAdminUserService;
|
||||
|
||||
/**
|
||||
* 重置admin密码
|
||||
*/
|
||||
@Test
|
||||
public void resetAdminPwd() {
|
||||
String username = "admin";
|
||||
String defPassword = "123456";
|
||||
defPassword = DigestUtils.sha256Hex(defPassword);
|
||||
String encodedPassword = BCrypt.hashpw(defPassword, BCrypt.gensalt());
|
||||
System.out.println("数据库保存:" + encodedPassword);
|
||||
sysAdminUserService.query()
|
||||
.eq(SysUser::getUsername, username)
|
||||
.set(SysUser::getPassword, encodedPassword)
|
||||
.update();
|
||||
}
|
||||
|
||||
}
|
||||
97
sop-admin/sop-admin-backend/admin-common/pom.xml
Executable file
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-admin-backend</artifactId>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>admin-common</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-service-support</artifactId>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.gitee.durcframework</groupId>
|
||||
<artifactId>fastmybatis-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- json处理 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.oschina.durcframework</groupId>
|
||||
<artifactId>http-helper</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||