45 changed files with 1898 additions and 3839 deletions
@ -0,0 +1,25 @@ |
|||
-----BEGIN CERTIFICATE----- |
|||
MIIEITCCAwmgAwIBAgIUS0Kl7fnKL8Ks6yEnGnc6TxW0MqQwDQYJKoZIhvcNAQEL |
|||
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT |
|||
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg |
|||
Q0EwHhcNMjYwMjEwMDkzODU1WhcNMzEwMjA5MDkzODU1WjB7MRMwEQYDVQQDDAox |
|||
MTA2Mzc1OTYwMRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xJzAlBgNVBAsM |
|||
Huays+WMl+W/q+aLvuenkeaKgOaciemZkOWFrOWPuDELMAkGA1UEBhMCQ04xETAP |
|||
BgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA |
|||
4DGM21ZX1weGlJPPCTMfHPiL1jmxG7cuTHwS37bfzWB4T+SmfOuwEfYILeY7iFCr |
|||
l14yr89wvlzfp4kwCIV55FEDBvsjp714JjAOIKv2piHXD1Sw+MhHo0QdGnNYBl+H |
|||
alkVpg/wSBkP43f3mXc5+EMFOKY6ET0ytXHx1AIILoACvNU70SPfIsOkU6h8CAg+ |
|||
/ocIDEm5Z9i8wG8KLlvNX5/M40nraVwJzMLsU0mx4BVe0MNRQ4fj/DfLgijKpk7R |
|||
juK3XlRycX7bgg8oIO7KHPY/H4iDYpAKSH8Psq+h6Jjab4IIYITpqNurvmSa+VDR |
|||
RAzJFAmxcW1xt8I0HnCJvQIDAQABo4G5MIG2MAkGA1UdEwQCMAAwCwYDVR0PBAQD |
|||
AgP4MIGbBgNVHR8EgZMwgZAwgY2ggYqggYeGgYRodHRwOi8vZXZjYS5pdHJ1cy5j |
|||
b20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3 |
|||
NTQ5ODQ2QzAxQzNFOEVCRDImc2c9SEFDQzQ3MUI2NTQyMkUxMkIyN0E5RDMzQTg3 |
|||
QUQxQ0RGNTkyNkUxNDAzNzEwDQYJKoZIhvcNAQELBQADggEBAKZasaeU83GcoPmX |
|||
XTq9tZz/IvgWUpRavJWiTT3aR8V/jMRbpVpH9qpZnUvzRxiGctePajBNY7QEWIxv |
|||
BgmGkXRqFcnSiD+nB3JW1pvGMsXZsJaKSCwpHW3JIsEhGkX5+idzwdyiV/xWFtYc |
|||
O54cS9dc67u1De0JPIByxoml6Uz1u7PfUfbAq9rUWo6ufSV+va7cpOPAtgQsPJn1 |
|||
3vqKVJClPOVAEX/ge7k11LcZtB0jAyWh9M2HA91n1oa4U1psW+tgEAAlyz2cpSSA |
|||
YfcYi1J0Isx9lX4trC0hKgj+2kWJ7etgFe8bEhlMwYn4ZiYEejWW8BfcwQifLVn8 |
|||
TDuL0m0= |
|||
-----END CERTIFICATE----- |
|||
@ -0,0 +1,28 @@ |
|||
-----BEGIN PRIVATE KEY----- |
|||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDgMYzbVlfXB4aU |
|||
k88JMx8c+IvWObEbty5MfBLftt/NYHhP5KZ867AR9ggt5juIUKuXXjKvz3C+XN+n |
|||
iTAIhXnkUQMG+yOnvXgmMA4gq/amIdcPVLD4yEejRB0ac1gGX4dqWRWmD/BIGQ/j |
|||
d/eZdzn4QwU4pjoRPTK1cfHUAggugAK81TvRI98iw6RTqHwICD7+hwgMSbln2LzA |
|||
bwouW81fn8zjSetpXAnMwuxTSbHgFV7Qw1FDh+P8N8uCKMqmTtGO4rdeVHJxftuC |
|||
Dygg7soc9j8fiINikApIfw+yr6HomNpvgghghOmo26u+ZJr5UNFEDMkUCbFxbXG3 |
|||
wjQecIm9AgMBAAECggEBALfirH/zMBUlDROssLIBBlIC4t+Rbl0nQIhndCuema6g |
|||
o84T4yKvRjlVLZxILSg/0p5TGwvs/7KEBsYp1gYHRNUqRWtibfpVg8j+vXe61JGr |
|||
S/Q9KPLFg0y8v4pEdTy0+iMWcpVEmXbpZ4jRi3qKujeQ8SVn4lTld0Qv84RLLl7E |
|||
A1YHVWjA3B6av0OGhDlbguLu2vh485Ks5VyepVHuGGhrEQis5KMClbGw/IZGTBCw |
|||
PMGm7BP4MmDTxdd+5aJ+2yAn6+dgp2Tt51Cm1WuUhB8cPetqtDzJLNgDE7fp0lUM |
|||
WWD9drYbQaJlWEWILyBJfFffRc+/ppwSSJ3E4GdU2UECgYEA9iPqDQZvoFOdBeDi |
|||
iB+K38razmfannuH23erxvP9iJq2uoT/Us6Y3ioM8dV0IvKcNPPmZa+nz9zziJEd |
|||
FbbN00cWLzFYybBNgnBBCIDTN7c5lsbIYhR3s2TXN5fI/4zLRxcJzbNvbuFFZyq6 |
|||
6AYo6jXUHA7R7nKin5dc1W0xNWkCgYEA6SyUYDJo3IFqC8/GcJH+4WK8g143jk8n |
|||
PyKWRuRwB+mOHKC6TC1rqX719L32FYm3xe2ltyEjK47XHN/Ie/+vLmLCRfsvMdHO |
|||
t3o5w4cHbZ03tFvyqUP5Ei5DrB/AUvIS3fKRjDh4lk7khWOvFkNwJ731Ba68CprG |
|||
383TyAlWQzUCgYEAuYme7syQLkF41qqK9+MW8tTdlMMSN26UnSmbEbBvx54f6X9B |
|||
WzEiaC04br7g+Ur51qyXWsVK8NPzu1jvnKOciQtHvLEs5XOBKbbmPruk+5Wg0nfr |
|||
KouVI2P7GwvOVlvSCzdhi24brHAgucCq/SVPiCSlS7UcJ+q/jR8yuirB8ikCgYBs |
|||
a38eb7IUfdRaY6UoqKn0EN4I02FPuXxNPf7UPdndw/qBUzbsvt3ltRQWVdG72Aps |
|||
bQD18uGQml1pnqBxD8Vb8y3ULmSWbLEK3TlIsluA226QXYSqseF5U6vBuA3MQ6UE |
|||
MIr+wS9I7KwTXfOGjZrzz77DgqkK4UcBv4nu5HCI1QKBgGDZYIZ4DGkBSNCjQaC9 |
|||
x0l7oHFtntUj13vGQINvx1yaKm7SJS0Vbj6Oa75qmQtDQUSuF6SDImNy4+X1Oz1Q |
|||
J43IsnQoSGJ2YjU4qXyhXmjd4jXeeG0J6Tnvc2Ad+WToBjhsXBhQymKhU2zDy0XG |
|||
MzUWVMBqAPae3sijI8n1INyW |
|||
-----END PRIVATE KEY----- |
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 434 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,2 +0,0 @@ |
|||
/*! json-tree - v0.2.2 - 2017-09-25, MIT LICENSE */ |
|||
var JSONTree=function(){var n={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},t=0,r=0;this.create=function(n,t){return r+=1,N(u(n,0,!1),{class:"jstValue"})};var e=function(t){return t.replace(/[&<>'"]/g,function(t){return n[t]})},s=function(){return r+"_"+t++},u=function(n,t,r){if(null===n)return f(r?t:0);switch(typeof n){case"boolean":return l(n,r?t:0);case"number":return i(n,r?t:0);case"string":return o(n,r?t:0);default:return n instanceof Array?a(n,t,r):c(n,t,r)}},c=function(n,t,r){var e=s(),u=Object.keys(n).map(function(r){return j(r,n[r],t+1,!0)}).join(m()),c=[g("{",r?t:0,e),N(u,{id:e}),p("}",t)].join("\n");return N(c,{})},a=function(n,t,r){var e=s(),c=n.map(function(n){return u(n,t+1,!0)}).join(m());return[g("[",r?t:0,e),N(c,{id:e}),p("]",t)].join("\n")},o=function(n,t){var r=e(JSON.stringify(n));return N(v(r,t),{class:"jstStr"})},i=function(n,t){return N(v(n,t),{class:"jstNum"})},l=function(n,t){return N(v(n,t),{class:"jstBool"})},f=function(n){return N(v("null",n),{class:"jstNull"})},j=function(n,t,r){var s=v(e(JSON.stringify(n))+": ",r),c=N(u(t,r,!1),{});return N(s+c,{class:"jstProperty"})},m=function(){return N(",\n",{class:"jstComma"})},N=function(n,t){return d("span",t,n)},d=function(n,t,r){return"<"+n+Object.keys(t).map(function(n){return" "+n+'="'+t[n]+'"'}).join("")+">"+r+"</"+n+">"},g=function(n,t,r){return N(v(n,t),{class:"jstBracket"})+N("",{class:"jstFold",onclick:"JSONTree.toggle('"+r+"')"})};this.toggle=function(n){var t=document.getElementById(n),r=t.parentNode,e=t.previousElementSibling;""===t.className?(t.className="jstHiddenBlock",r.className="jstFolded",e.className="jstExpand"):(t.className="",r.className="",e.className="jstFold")};var p=function(n,t){return N(v(n,t),{})},v=function(n,t){return Array(2*t+1).join(" ")+n};return this}(); |
|||
|
Before Width: | Height: | Size: 3.7 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,233 +1,233 @@ |
|||
|
|||
|
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
|||
<title>Hiver测试报告</title> |
|||
<link rel="apple-touch-icon" href="spark/logo.png"> |
|||
<link rel="shortcut icon" href="spark/logo.png"> |
|||
<link rel="stylesheet" href="spark/spark-style.css"> |
|||
<link rel="stylesheet" href="spark/font-awesome.min.css"> |
|||
<script src="spark/jsontree.js"></script> |
|||
<style type="text/css"></style></head><body class="spa -report dark"> |
|||
<div class="app"> |
|||
<div class="layout"> |
|||
<div class="header navbar"> |
|||
<div class="vheader"> |
|||
<div class="nav-logo"> |
|||
<a href="#"> |
|||
<div class="logo" style="background-image: url('spark/logo.png')"></div> |
|||
</a> |
|||
</div> |
|||
<ul class="nav-left"> |
|||
<li class="search-box"> |
|||
<a class="search-toggle" href="#"> |
|||
<i class="search-icon fa fa-search"></i> |
|||
<i class="search-icon-close fa fa-close"></i> |
|||
</a> |
|||
</li> |
|||
<li class="search-input"><input id="search-tests" class="form-control" type="text" placeholder="Search..."></li> |
|||
</ul> |
|||
<ul class="nav-right"> |
|||
<li class="m-r-10"> |
|||
<a href="#"><span class="badge badge-primary">Hiver</span></a> |
|||
</li> |
|||
<li class="m-r-10"> |
|||
<a href="#"><span class="badge badge-primary">二月 12, 2026 15:24:12</span></a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div><div class="side-nav"> |
|||
<div class="side-nav-inner"> |
|||
<ul class="side-nav-menu"> |
|||
<li class="nav-item dropdown" onclick="toggleView('test-view')"> |
|||
<a id="nav-test" class="dropdown-toggle" href="#"> |
|||
<span class="ico"><i class="fa fa-list"></i></span> |
|||
</a> |
|||
</li> |
|||
<li class="nav-item dropdown" onclick="toggleView('dashboard-view')"> |
|||
<a id="nav-dashboard" class="dropdown-toggle" href="#"> |
|||
<span class="ico"><i class="fa fa-bar-chart"></i></span> |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> <div class="vcontainer"> |
|||
<div class="main-content"> |
|||
<div class="test-wrapper row view test-view"> |
|||
<div class="test-list"> |
|||
<div class="test-list-tools"> |
|||
<ul class="tools pull-left"> |
|||
<li><a href="#"><span class="font-size-14">Tests</span></a></li> |
|||
</ul> |
|||
<ul class="tools text-right"> |
|||
<li class="dropdown"> |
|||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-exclamation-circle"></i></a> |
|||
<ul id="status-toggle" class="dropdown-menu dropdown-md p-v-0"> |
|||
<a class="dropdown-item" status="pass" href="#"><span>Pass</span><span class="status success"></span></a> |
|||
<div class="dropdown-divider"></div> |
|||
<a status="clear" class="dropdown-item" href="#"><span>Clear</span><span class="pull-right"><i class="fa fa-close"></i></span></a> |
|||
</ul> |
|||
</li> |
|||
</ul> |
|||
</div> <div class="test-list-wrapper scrollable"> |
|||
<ul class="test-list-item"> |
|||
<li class="test-item" status="pass" test-id="1" |
|||
author="" |
|||
tag="" |
|||
device=""> |
|||
<div class="status-avatar pass-bg"> |
|||
<i class="fa fa-check text-white"></i> |
|||
</div> |
|||
<div class="test-detail"> |
|||
<span class="meta text-white badge badge-sm"></span> |
|||
<p class="name">passTest</p> |
|||
<p class="text-sm"><span>15:24:12 下午</span> / <span>0.015 secs</span></p> |
|||
</div> |
|||
<div class="test-contents d-none"> |
|||
<div class="detail-head"> |
|||
<div class="p-v-10"> |
|||
<div class="info"> |
|||
<div class='float-right'><span class='badge badge-default'>#test-id=1</span></div> |
|||
<h5 class="test-status text-pass">passTest</h5> |
|||
<span class='badge badge-success'>02.12.2026 15:24:12</span> |
|||
<span class='badge badge-danger'>02.12.2026 15:24:12</span> |
|||
<span class='badge badge-default'>0.015 secs</span> |
|||
</div> |
|||
<div class="m-t-10 m-l-5"></div> |
|||
</div> |
|||
</div><div class="detail-body mt-4"> |
|||
<table class="table table-sm"> |
|||
<thead><tr><th class="status-col">Status</th><th class="timestamp-col">Timestamp</th><th class="details-col">Details</th></tr></thead> |
|||
<tbody> |
|||
<tr class="event-row"> |
|||
<td><span class="badge log pass-bg">Pass</span></td> |
|||
<td>15:24:12</td> |
|||
<td> |
|||
Test passed |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
<div class="test-content scrollable"> |
|||
<div class="test-content-tools"> |
|||
<ul><li><a class="back-to-test" href="#"><i class="fa fa-arrow-left"></i></a></li></ul> |
|||
</div> |
|||
<div class="test-content-detail"><div class="detail-body"></div></div> |
|||
</div></div> |
|||
<div class="container-fluid p-4 view dashboard-view"> |
|||
<div class="row"> |
|||
<div class="col-md-3"> |
|||
<div class="card"><div class="card-body"> |
|||
<p class="m-b-0">Started</p> |
|||
<h3>二月 12, 2026 15:24:12</h3> |
|||
</div></div> |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<div class="card"><div class="card-body"> |
|||
<p class="m-b-0">Ended</p> |
|||
<h3>二月 12, 2026 15:24:12</h3> |
|||
</div></div> |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<div class="card"><div class="card-body"> |
|||
<p class="m-b-0 text-pass">Tests Passed</p> |
|||
<h3>1</h3> |
|||
</div></div> |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<div class="card"><div class="card-body"> |
|||
<p class="m-b-0 text-fail">Tests Failed</p> |
|||
<h3>0</h3> |
|||
</div></div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-md-6"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
<h6 class="card-title">Tests</h6> |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class=""> |
|||
<canvas id='parent-analysis' width='115' height='90'></canvas> |
|||
</div> |
|||
</div> |
|||
<div class="card-footer"> |
|||
<div><small data-tooltip='100%'> |
|||
<b>1</b> tests passed |
|||
</small> |
|||
</div> |
|||
<div> |
|||
<small data-tooltip='0%'><b>0</b> tests failed, |
|||
<b>0</b> skipped, <b data-tooltip='0%'>0</b> others |
|||
</small> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-6"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
<h6 class="card-title">Log events</h6> |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class=""> |
|||
<canvas id='events-analysis' width='115' height='90'></canvas> |
|||
</div> |
|||
</div> |
|||
<div class="card-footer"> |
|||
<div><small data-tooltip='100%'><b>1</b> events passed</small></div> |
|||
<div> |
|||
<small data-tooltip='0%'><b>0</b> events failed, |
|||
<b data-tooltip='%'>0</b> others |
|||
</small> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
</div> |
|||
</div> |
|||
<script> |
|||
var statusGroup = { |
|||
parentCount: 5, |
|||
passParent: 1, |
|||
failParent: 0, |
|||
warningParent: 0, |
|||
skipParent: 0, |
|||
childCount: 5, |
|||
passChild: 0, |
|||
failChild: 0, |
|||
warningChild: 0, |
|||
skipChild: 0, |
|||
infoChild: 0, |
|||
grandChildCount: 5, |
|||
passGrandChild: 0, |
|||
failGrandChild: 0, |
|||
warningGrandChild: 0, |
|||
skipGrandChild: 0, |
|||
infoGrandChild: 0, |
|||
eventsCount: 5, |
|||
passEvents: 1, |
|||
failEvents: 0, |
|||
warningEvents: 0, |
|||
skipEvents: 0, |
|||
infoEvents: 0 |
|||
}; |
|||
</script> </div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<script src="spark/spark-script.js"></script> |
|||
<script type="text/javascript"></script></body> |
|||
|
|||
|
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
|||
<title>Hiver测试报告</title> |
|||
<link rel="apple-touch-icon" href="spark/logo.png"> |
|||
<link rel="shortcut icon" href="spark/logo.png"> |
|||
<link rel="stylesheet" href="spark/spark-style.css"> |
|||
<link rel="stylesheet" href="spark/font-awesome.min.css"> |
|||
<script src="spark/jsontree.js"></script> |
|||
<style type="text/css"></style></head><body class="spa -report dark"> |
|||
<div class="app"> |
|||
<div class="layout"> |
|||
<div class="header navbar"> |
|||
<div class="vheader"> |
|||
<div class="nav-logo"> |
|||
<a href="#"> |
|||
<div class="logo" style="background-image: url('spark/logo.png')"></div> |
|||
</a> |
|||
</div> |
|||
<ul class="nav-left"> |
|||
<li class="search-box"> |
|||
<a class="search-toggle" href="#"> |
|||
<i class="search-icon fa fa-search"></i> |
|||
<i class="search-icon-close fa fa-close"></i> |
|||
</a> |
|||
</li> |
|||
<li class="search-input"><input id="search-tests" class="form-control" type="text" placeholder="Search..."></li> |
|||
</ul> |
|||
<ul class="nav-right"> |
|||
<li class="m-r-10"> |
|||
<a href="#"><span class="badge badge-primary">Hiver</span></a> |
|||
</li> |
|||
<li class="m-r-10"> |
|||
<a href="#"><span class="badge badge-primary">二月 12, 2026 15:24:12</span></a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div><div class="side-nav"> |
|||
<div class="side-nav-inner"> |
|||
<ul class="side-nav-menu"> |
|||
<li class="nav-item dropdown" onclick="toggleView('test-view')"> |
|||
<a id="nav-test" class="dropdown-toggle" href="#"> |
|||
<span class="ico"><i class="fa fa-list"></i></span> |
|||
</a> |
|||
</li> |
|||
<li class="nav-item dropdown" onclick="toggleView('dashboard-view')"> |
|||
<a id="nav-dashboard" class="dropdown-toggle" href="#"> |
|||
<span class="ico"><i class="fa fa-bar-chart"></i></span> |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> <div class="vcontainer"> |
|||
<div class="main-content"> |
|||
<div class="test-wrapper row view test-view"> |
|||
<div class="test-list"> |
|||
<div class="test-list-tools"> |
|||
<ul class="tools pull-left"> |
|||
<li><a href="#"><span class="font-size-14">Tests</span></a></li> |
|||
</ul> |
|||
<ul class="tools text-right"> |
|||
<li class="dropdown"> |
|||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-exclamation-circle"></i></a> |
|||
<ul id="status-toggle" class="dropdown-menu dropdown-md p-v-0"> |
|||
<a class="dropdown-item" status="pass" href="#"><span>Pass</span><span class="status success"></span></a> |
|||
<div class="dropdown-divider"></div> |
|||
<a status="clear" class="dropdown-item" href="#"><span>Clear</span><span class="pull-right"><i class="fa fa-close"></i></span></a> |
|||
</ul> |
|||
</li> |
|||
</ul> |
|||
</div> <div class="test-list-wrapper scrollable"> |
|||
<ul class="test-list-item"> |
|||
<li class="test-item" status="pass" test-id="1" |
|||
author="" |
|||
tag="" |
|||
device=""> |
|||
<div class="status-avatar pass-bg"> |
|||
<i class="fa fa-check text-white"></i> |
|||
</div> |
|||
<div class="test-detail"> |
|||
<span class="meta text-white badge badge-sm"></span> |
|||
<p class="name">passTest</p> |
|||
<p class="text-sm"><span>15:24:12 下午</span> / <span>0.015 secs</span></p> |
|||
</div> |
|||
<div class="test-contents d-none"> |
|||
<div class="detail-head"> |
|||
<div class="p-v-10"> |
|||
<div class="info"> |
|||
<div class='float-right'><span class='badge badge-default'>#test-id=1</span></div> |
|||
<h5 class="test-status text-pass">passTest</h5> |
|||
<span class='badge badge-success'>02.12.2026 15:24:12</span> |
|||
<span class='badge badge-danger'>02.12.2026 15:24:12</span> |
|||
<span class='badge badge-default'>0.015 secs</span> |
|||
</div> |
|||
<div class="m-t-10 m-l-5"></div> |
|||
</div> |
|||
</div><div class="detail-body mt-4"> |
|||
<table class="table table-sm"> |
|||
<thead><tr><th class="status-col">Status</th><th class="timestamp-col">Timestamp</th><th class="details-col">Details</th></tr></thead> |
|||
<tbody> |
|||
<tr class="event-row"> |
|||
<td><span class="badge log pass-bg">Pass</span></td> |
|||
<td>15:24:12</td> |
|||
<td> |
|||
Test passed |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
<div class="test-content scrollable"> |
|||
<div class="test-content-tools"> |
|||
<ul><li><a class="back-to-test" href="#"><i class="fa fa-arrow-left"></i></a></li></ul> |
|||
</div> |
|||
<div class="test-content-detail"><div class="detail-body"></div></div> |
|||
</div></div> |
|||
<div class="container-fluid p-4 view dashboard-view"> |
|||
<div class="row"> |
|||
<div class="col-md-3"> |
|||
<div class="card"><div class="card-body"> |
|||
<p class="m-b-0">Started</p> |
|||
<h3>二月 12, 2026 15:24:12</h3> |
|||
</div></div> |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<div class="card"><div class="card-body"> |
|||
<p class="m-b-0">Ended</p> |
|||
<h3>二月 12, 2026 15:24:12</h3> |
|||
</div></div> |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<div class="card"><div class="card-body"> |
|||
<p class="m-b-0 text-pass">Tests Passed</p> |
|||
<h3>1</h3> |
|||
</div></div> |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<div class="card"><div class="card-body"> |
|||
<p class="m-b-0 text-fail">Tests Failed</p> |
|||
<h3>0</h3> |
|||
</div></div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-md-6"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
<h6 class="card-title">Tests</h6> |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class=""> |
|||
<canvas id='parent-analysis' width='115' height='90'></canvas> |
|||
</div> |
|||
</div> |
|||
<div class="card-footer"> |
|||
<div><small data-tooltip='100%'> |
|||
<b>1</b> tests passed |
|||
</small> |
|||
</div> |
|||
<div> |
|||
<small data-tooltip='0%'><b>0</b> tests failed, |
|||
<b>0</b> skipped, <b data-tooltip='0%'>0</b> others |
|||
</small> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-6"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
<h6 class="card-title">Log events</h6> |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class=""> |
|||
<canvas id='events-analysis' width='115' height='90'></canvas> |
|||
</div> |
|||
</div> |
|||
<div class="card-footer"> |
|||
<div><small data-tooltip='100%'><b>1</b> events passed</small></div> |
|||
<div> |
|||
<small data-tooltip='0%'><b>0</b> events failed, |
|||
<b data-tooltip='%'>0</b> others |
|||
</small> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
</div> |
|||
</div> |
|||
<script> |
|||
var statusGroup = { |
|||
parentCount: 5, |
|||
passParent: 1, |
|||
failParent: 0, |
|||
warningParent: 0, |
|||
skipParent: 0, |
|||
childCount: 5, |
|||
passChild: 0, |
|||
failChild: 0, |
|||
warningChild: 0, |
|||
skipChild: 0, |
|||
infoChild: 0, |
|||
grandChildCount: 5, |
|||
passGrandChild: 0, |
|||
failGrandChild: 0, |
|||
warningGrandChild: 0, |
|||
skipGrandChild: 0, |
|||
infoGrandChild: 0, |
|||
eventsCount: 5, |
|||
passEvents: 1, |
|||
failEvents: 0, |
|||
warningEvents: 0, |
|||
skipEvents: 0, |
|||
infoEvents: 0 |
|||
}; |
|||
</script> </div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<script src="spark/spark-script.js"></script> |
|||
<script type="text/javascript"></script></body> |
|||
</html> |
|||
@ -0,0 +1,69 @@ |
|||
package cc.hiver.mall.controller; |
|||
|
|||
import cc.hiver.core.common.utils.ResultUtil; |
|||
import cc.hiver.core.common.utils.StringUtils; |
|||
import cc.hiver.core.common.vo.Result; |
|||
import cc.hiver.mall.entity.Areas; |
|||
import cc.hiver.mall.service.mybatis.AreasService; |
|||
import io.swagger.annotations.Api; |
|||
import io.swagger.annotations.ApiOperation; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestMethod; |
|||
import org.springframework.web.bind.annotation.RequestParam; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Slf4j |
|||
@RestController |
|||
@Api(tags = "区域数据字典接口") |
|||
@RequestMapping("/hiver/app/areas/") |
|||
@Transactional |
|||
public class AreasController { |
|||
@Autowired |
|||
private AreasService areasService; |
|||
|
|||
@RequestMapping(value = "/save", method = RequestMethod.POST) |
|||
@ApiOperation("新增") |
|||
@Transactional |
|||
public Result save(Areas areas) { |
|||
try { |
|||
final Areas addAreas = areasService.insert(areas); |
|||
// 添加成功,返回信息
|
|||
return new ResultUtil<Areas>().setData(addAreas); |
|||
}catch (Exception e){ |
|||
log.error("添加区域数据字典失败", e); |
|||
return ResultUtil.error("添加失败"); |
|||
} |
|||
} |
|||
|
|||
@RequestMapping(value = "/edit", method = RequestMethod.POST) |
|||
@ApiOperation("根据id修改区域数据字典") |
|||
public Result edit(Areas areas) { |
|||
if (StringUtils.isEmpty(areas.getId())) { |
|||
return ResultUtil.error("id不能为空"); |
|||
} |
|||
final Areas updateAreas = areasService.updateByAreasId(areas); |
|||
return new ResultUtil<Areas>().setData(updateAreas); |
|||
} |
|||
|
|||
@RequestMapping(value = "/delById", method = RequestMethod.POST) |
|||
@ApiOperation("根据id删除") |
|||
public Result delById(@RequestParam String id,@RequestParam Integer delFlag) { |
|||
if (StringUtils.isEmpty(id)) { |
|||
return ResultUtil.error("id不能为空"); |
|||
} |
|||
areasService.delById(id,delFlag); |
|||
return ResultUtil.success("删除成功"); |
|||
} |
|||
|
|||
@RequestMapping(value = "/listBySchoolId", method = RequestMethod.GET) |
|||
@ApiOperation("根据学校id查询列表") |
|||
public Result listBySchoolId(@RequestParam String schoolId) { |
|||
final List<Areas> list = areasService.listBySchoolId(schoolId); |
|||
return new ResultUtil<List<Areas>>().setData(list); |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
/* |
|||
Copyright [2022] [https://hiver.cc]
|
|||
|
|||
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. |
|||
*/ |
|||
package cc.hiver.mall.controller; |
|||
|
|||
import cc.hiver.core.common.utils.ResultUtil; |
|||
import cc.hiver.core.common.vo.Result; |
|||
import cc.hiver.mall.entity.ProductGroupBuyPrice; |
|||
import cc.hiver.mall.service.mybatis.ProductGroupBuyPriceService; |
|||
import io.swagger.annotations.Api; |
|||
import io.swagger.annotations.ApiOperation; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 店铺外卖业务配置控制器 |
|||
* |
|||
* @author cc |
|||
*/ |
|||
@Slf4j |
|||
@RestController |
|||
@Api(tags = "商品拼团价格接口") |
|||
@RequestMapping("/hiver/app/productGroupBuyPrice") |
|||
public class ProductGroupBuyPriceController { |
|||
|
|||
@Autowired |
|||
private ProductGroupBuyPriceService productGroupBuyPriceService; |
|||
|
|||
@RequestMapping(value = "/getByProductId", method = RequestMethod.GET) |
|||
@ApiOperation("根据商品id获取拼团配置") |
|||
public Result<List<ProductGroupBuyPrice>> getByProductId(@RequestParam String productId) { |
|||
final List<ProductGroupBuyPrice> productGroupBuyPriceList = productGroupBuyPriceService.selectByProductId(productId); |
|||
return new ResultUtil<List<ProductGroupBuyPrice>>().setData(productGroupBuyPriceList); |
|||
} |
|||
|
|||
@PostMapping(value = "/batchAddproductGroupBuyPrice") |
|||
@ApiOperation("批量新增") |
|||
public Result batchAddproductGroupBuyPrice(@RequestBody List<ProductGroupBuyPrice> productGroupBuyPrices) { |
|||
if(productGroupBuyPrices != null && productGroupBuyPrices.size() > 0){ |
|||
productGroupBuyPriceService.deleteByProductId(productGroupBuyPrices.get(0).getProductId()); |
|||
} |
|||
// 批量新增
|
|||
final boolean b = productGroupBuyPriceService.saveBatch(productGroupBuyPrices); |
|||
if (b) { |
|||
return ResultUtil.success("保存成功!"); |
|||
} else { |
|||
return ResultUtil.error("保存失败!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,99 @@ |
|||
/* |
|||
Copyright [2022] [https://hiver.cc]
|
|||
|
|||
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. |
|||
*/ |
|||
package cc.hiver.mall.controller; |
|||
|
|||
import cc.hiver.core.common.utils.ResultUtil; |
|||
import cc.hiver.core.common.vo.Result; |
|||
import cc.hiver.mall.entity.Sale; |
|||
import cc.hiver.mall.entity.ShopTakeaway; |
|||
import cc.hiver.mall.pojo.query.SalePageQuery; |
|||
import cc.hiver.mall.pojo.query.ShopTakeawayQuery; |
|||
import cc.hiver.mall.service.ShopTakeawayService; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import io.swagger.annotations.Api; |
|||
import io.swagger.annotations.ApiOperation; |
|||
import io.swagger.annotations.ApiParam; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 店铺外卖业务配置控制器 |
|||
* |
|||
* @author cc |
|||
*/ |
|||
@Slf4j |
|||
@RestController |
|||
@Api(tags = "店铺外卖业务配置接口") |
|||
@RequestMapping("/hiver/app/shoptakeaway") |
|||
public class ShopTakeawayController { |
|||
|
|||
@Autowired |
|||
private ShopTakeawayService shopTakeawayService; |
|||
|
|||
@RequestMapping(value = "/getByShopId", method = RequestMethod.GET) |
|||
@ApiOperation("根据店铺id获取外卖业务配置") |
|||
public Result<ShopTakeaway> getByShopId( |
|||
@ApiParam("店铺id") @RequestParam String shopId) { |
|||
final ShopTakeaway shopTakeaway = shopTakeawayService.selectByPrimaryKey(shopId); |
|||
return new ResultUtil<ShopTakeaway>().setData(shopTakeaway); |
|||
} |
|||
|
|||
@RequestMapping(value = "/save", method = RequestMethod.POST) |
|||
@ApiOperation("保存或更新外卖业务配置") |
|||
@ResponseBody |
|||
public Result<ShopTakeaway> save(ShopTakeaway shopTakeaway) { |
|||
// 1. 获取当前时间
|
|||
LocalDateTime now = LocalDateTime.now(); |
|||
|
|||
// 2. 定义格式化器 (例如:yyyy-MM-dd HH:mm:ss)
|
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
|||
|
|||
// 3. 转换为字符串
|
|||
String timeStr = now.format(formatter); |
|||
shopTakeaway.setJoinTime(timeStr); |
|||
final ShopTakeaway result = shopTakeawayService.insert(shopTakeaway); |
|||
return new ResultUtil<ShopTakeaway>().setData(result); |
|||
} |
|||
|
|||
@RequestMapping(value = "/update", method = RequestMethod.POST) |
|||
@ApiOperation("保存或更新外卖业务配置") |
|||
@ResponseBody |
|||
public Result<ShopTakeaway> update(ShopTakeaway shopTakeaway) { |
|||
shopTakeawayService.updateByPrimaryKeySelective(shopTakeaway); |
|||
return ResultUtil.success("更新成功!"); |
|||
} |
|||
|
|||
@RequestMapping(value = "/deleteByShopId", method = RequestMethod.POST) |
|||
@ApiOperation("删除外卖业务配置") |
|||
public Result<Object> deleteByShopId( |
|||
@ApiParam("店铺id") @RequestParam String shopId) { |
|||
shopTakeawayService.deleteByPrimaryKey(shopId); |
|||
return ResultUtil.success("删除成功"); |
|||
} |
|||
|
|||
@RequestMapping(value = "/getList", method = RequestMethod.POST) |
|||
@ApiOperation("根据学校id获取外卖业务配置列表") |
|||
public Result<Page<ShopTakeaway>> getList( |
|||
@ApiParam("店铺id列表") @RequestBody ShopTakeawayQuery shopTakeawayQuery) { |
|||
final Page<ShopTakeaway> shopTakeawaysPage = shopTakeawayService.selectList(shopTakeawayQuery); |
|||
return ResultUtil.data(shopTakeawaysPage); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
package cc.hiver.mall.dao.mapper; |
|||
|
|||
import cc.hiver.mall.entity.Areas; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import org.apache.ibatis.annotations.Param; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Repository |
|||
public interface AreasMapper extends BaseMapper<Areas> { |
|||
|
|||
List<Areas> listBySchoolId(String schoolId); |
|||
|
|||
int insert(Areas areas); |
|||
|
|||
int updateByAreasId(@Param("areas") Areas areas); |
|||
|
|||
void delById(@Param("id") String id,@Param("delFlag") Integer delFlag); |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
package cc.hiver.mall.dao.mapper; |
|||
|
|||
import cc.hiver.mall.entity.Comment; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
@Repository |
|||
public interface CommentMapper extends BaseMapper<Comment> { |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
package cc.hiver.mall.dao.mapper; |
|||
|
|||
import cc.hiver.mall.entity.ProductGroupBuyPrice; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Repository |
|||
public interface ProductGroupBuyPriceMapper extends BaseMapper<ProductGroupBuyPrice> { |
|||
|
|||
int insert(ProductGroupBuyPrice record); |
|||
|
|||
List<ProductGroupBuyPrice> selectByProductId(String productId); |
|||
|
|||
void deleteByProductId(String productId); |
|||
|
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/* |
|||
Copyright [2022] [https://hiver.cc]
|
|||
|
|||
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. |
|||
*/ |
|||
package cc.hiver.mall.dao.mapper; |
|||
|
|||
import cc.hiver.mall.entity.Sale; |
|||
import cc.hiver.mall.entity.ShopTakeaway; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import org.apache.ibatis.annotations.Param; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 店铺外卖业务配置Mapper |
|||
* |
|||
* @author cc |
|||
*/ |
|||
@Repository |
|||
public interface ShopTakeawayMapper extends BaseMapper<ShopTakeaway> { |
|||
|
|||
ShopTakeaway selectByPrimaryKey(String shopId); |
|||
|
|||
void deleteByPrimaryKey(String shopId); |
|||
|
|||
int insert(ShopTakeaway shopTakeaway); |
|||
|
|||
void updateByPrimaryKeySelective(ShopTakeaway shopTakeaway); |
|||
|
|||
Page<ShopTakeaway> selectList(Page<ShopTakeaway> page,@Param("regionId")String regionId); |
|||
|
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
package cc.hiver.mall.entity; |
|||
|
|||
import cc.hiver.core.base.HiverBaseEntity; |
|||
import cc.hiver.core.common.utils.SnowFlakeUtil; |
|||
import com.baomidou.mybatisplus.annotation.TableField; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
import org.hibernate.annotations.DynamicInsert; |
|||
import org.hibernate.annotations.DynamicUpdate; |
|||
|
|||
import javax.persistence.Entity; |
|||
import javax.persistence.Table; |
|||
import java.io.Serializable; |
|||
import java.math.BigDecimal; |
|||
import java.util.Date; |
|||
|
|||
@Data |
|||
@Entity |
|||
@DynamicInsert |
|||
@DynamicUpdate |
|||
@Table(name = "areas") |
|||
@TableName("areas") |
|||
@ApiModel(value = "客户地址表") |
|||
public class Areas extends HiverBaseEntity { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
private String id = SnowFlakeUtil.nextId().toString(); |
|||
|
|||
private Integer delFlag; |
|||
|
|||
@ApiModelProperty(value = "名称") |
|||
private String name; |
|||
|
|||
@ApiModelProperty(value = "备注") |
|||
private String remark; |
|||
|
|||
@ApiModelProperty(value = "排序字段") |
|||
private String orderByField; |
|||
|
|||
@ApiModelProperty(value = "学校Id") |
|||
private String schoolId; |
|||
|
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
package cc.hiver.mall.entity; |
|||
|
|||
import cc.hiver.core.base.HiverBaseEntity; |
|||
import cc.hiver.core.common.utils.SnowFlakeUtil; |
|||
import com.baomidou.mybatisplus.annotation.TableField; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
import org.hibernate.annotations.DynamicInsert; |
|||
import org.hibernate.annotations.DynamicUpdate; |
|||
|
|||
import javax.persistence.Entity; |
|||
import javax.persistence.Id; |
|||
import javax.persistence.Table; |
|||
import java.io.Serializable; |
|||
import java.math.BigDecimal; |
|||
import java.util.Date; |
|||
|
|||
@Data |
|||
@ApiModel(value = "客户表") |
|||
@TableName(value = "t_comment", autoResultMap = true) |
|||
public class Comment implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
private String id = SnowFlakeUtil.nextId().toString(); |
|||
|
|||
@ApiModelProperty(value = "上级id") |
|||
private String linkedId; |
|||
|
|||
@ApiModelProperty(value = "备注") |
|||
private String remark; |
|||
|
|||
@ApiModelProperty(value = "等级 0 一级评论 1回复评论") |
|||
private Integer level; |
|||
|
|||
@ApiModelProperty(value = "图片") |
|||
private String picture; |
|||
|
|||
@ApiModelProperty(value = "创建时间") |
|||
private String createTime; |
|||
|
|||
@ApiModelProperty(value = "创建人id") |
|||
private String createBy; |
|||
|
|||
@ApiModelProperty(value = "创建人名称") |
|||
private String createByName; |
|||
|
|||
@ApiModelProperty(value = "创建人头像") |
|||
private String createByIcon; |
|||
|
|||
@ApiModelProperty(value = "评分") |
|||
private String score; |
|||
|
|||
@ApiModelProperty(value = "订单id") |
|||
private String orderId; |
|||
|
|||
@ApiModelProperty(value = "店铺id") |
|||
private String shopId; |
|||
|
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
/* |
|||
Copyright [2022] [https://hiver.cc]
|
|||
|
|||
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. |
|||
*/ |
|||
package cc.hiver.mall.entity; |
|||
|
|||
import cc.hiver.core.base.HiverBaseEntity; |
|||
import com.baomidou.mybatisplus.annotation.TableField; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
import org.hibernate.annotations.DynamicInsert; |
|||
import org.hibernate.annotations.DynamicUpdate; |
|||
|
|||
import javax.persistence.Entity; |
|||
import javax.persistence.Table; |
|||
import java.math.BigDecimal; |
|||
|
|||
/** |
|||
* 店铺外卖业务配置实体类 |
|||
* |
|||
* @author cc |
|||
*/ |
|||
@Data |
|||
@Entity |
|||
@DynamicInsert |
|||
@DynamicUpdate |
|||
@Table(name = "t_product_group_buy_price") |
|||
@TableName("t_product_group_buy_price") |
|||
@ApiModel(value = "商品拼团价格配置") |
|||
public class ProductGroupBuyPrice extends HiverBaseEntity { |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@ApiModelProperty(value = "商品id") |
|||
private String productId; |
|||
|
|||
@ApiModelProperty(value = "成团人数") |
|||
private Integer groupCount; |
|||
|
|||
@ApiModelProperty(value = "成团价格") |
|||
private BigDecimal groupPrice; |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
/* |
|||
Copyright [2022] [https://hiver.cc]
|
|||
|
|||
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. |
|||
*/ |
|||
package cc.hiver.mall.entity; |
|||
|
|||
import cc.hiver.core.base.HiverBaseEntity; |
|||
import com.baomidou.mybatisplus.annotation.TableField; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
import org.hibernate.annotations.DynamicInsert; |
|||
import org.hibernate.annotations.DynamicUpdate; |
|||
|
|||
import javax.persistence.Entity; |
|||
import javax.persistence.Table; |
|||
import java.math.BigDecimal; |
|||
|
|||
/** |
|||
* 店铺外卖业务配置实体类 |
|||
* |
|||
* @author cc |
|||
*/ |
|||
@Data |
|||
@Entity |
|||
@DynamicInsert |
|||
@DynamicUpdate |
|||
@Table(name = "t_shop_takeaway") |
|||
@TableName("t_shop_takeaway") |
|||
@ApiModel(value = "店铺外卖业务配置") |
|||
public class ShopTakeaway extends HiverBaseEntity { |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@ApiModelProperty(value = "店铺id") |
|||
private String shopId; |
|||
|
|||
@ApiModelProperty(value = "店铺名称") |
|||
private String shopName; |
|||
|
|||
@ApiModelProperty(value = "所属学校") |
|||
private String regionId; |
|||
|
|||
@ApiModelProperty(value = "拼团合作佣金比例") |
|||
private BigDecimal commissionRateMore; |
|||
|
|||
@ApiModelProperty(value = "单独购买合作佣金比例") |
|||
private BigDecimal commissionRateOne; |
|||
|
|||
@TableField(exist = false) |
|||
@ApiModelProperty(value = "预计送达时间") |
|||
private String estimatedDeliveryTime; |
|||
|
|||
@ApiModelProperty(value = "营业开始时间") |
|||
private String businessHourBegin; |
|||
|
|||
@ApiModelProperty(value = "营业结束时间") |
|||
private String businessHourEnd; |
|||
|
|||
@ApiModelProperty(value = "状态 0正常营业 1暂停营业 2冻结") |
|||
private Integer status; |
|||
|
|||
@ApiModelProperty(value = "经营品类") |
|||
private String type; |
|||
|
|||
@ApiModelProperty(value = "是否支持配送 0支持 1不支持") |
|||
private Integer isDelivery; |
|||
|
|||
@ApiModelProperty(value = "出餐时间") |
|||
private Integer cookingTime; |
|||
|
|||
@ApiModelProperty(value = "排名") |
|||
private Integer shoprank; |
|||
|
|||
@ApiModelProperty(value = "是否价格锁定") |
|||
private Integer isPriceLock; |
|||
|
|||
@ApiModelProperty(value = "入驻时间") |
|||
private String joinTime; |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
package cc.hiver.mall.pojo.query; |
|||
|
|||
import cc.hiver.core.base.HiverBasePageQuery; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
@ApiModel("分享页查询对象") |
|||
@Data |
|||
public class ShopTakeawayQuery extends HiverBasePageQuery { |
|||
|
|||
@ApiModelProperty("学校id") |
|||
private String regionId; |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
package cc.hiver.mall.pojo.vo; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.math.BigDecimal; |
|||
|
|||
@Data |
|||
@ApiModel(value = "客户商品购买数量Vo") |
|||
public class CategoryNumberVo { |
|||
|
|||
@ApiModelProperty(value = "销售金额") |
|||
private BigDecimal totalAmount; |
|||
|
|||
@ApiModelProperty(value = "销售利润") |
|||
private BigDecimal totalProfit; |
|||
|
|||
@ApiModelProperty(value = "销售件数") |
|||
private Integer totalJCount; |
|||
|
|||
@ApiModelProperty(value = "进货金额") |
|||
private BigDecimal purchasingCost; |
|||
|
|||
@ApiModelProperty(value = "进货件数") |
|||
private Integer purchasingCount; |
|||
|
|||
@ApiModelProperty(value = "退货金额") |
|||
private BigDecimal returnTotalAmount; |
|||
|
|||
@ApiModelProperty(value = "退货成本") |
|||
private BigDecimal saleReturnCost; |
|||
|
|||
@ApiModelProperty(value = "退货件数") |
|||
private Integer returnTotalCount; |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/* |
|||
Copyright [2022] [https://hiver.cc]
|
|||
|
|||
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. |
|||
*/ |
|||
package cc.hiver.mall.service; |
|||
|
|||
import cc.hiver.core.base.HiverBaseService; |
|||
import cc.hiver.mall.entity.DealingsRecord; |
|||
import cc.hiver.mall.entity.Sale; |
|||
import cc.hiver.mall.entity.Shop; |
|||
import cc.hiver.mall.entity.ShopTakeaway; |
|||
import cc.hiver.mall.pojo.query.SalePageQuery; |
|||
import cc.hiver.mall.pojo.query.ShopTakeawayQuery; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 店铺外卖业务配置服务接口 |
|||
* |
|||
* @author cc |
|||
*/ |
|||
|
|||
public interface ShopTakeawayService extends IService<ShopTakeaway> { |
|||
|
|||
ShopTakeaway selectByPrimaryKey(String shopId); |
|||
|
|||
void deleteByPrimaryKey(String shopId); |
|||
|
|||
ShopTakeaway insert(ShopTakeaway shopTakeaway); |
|||
|
|||
void updateByPrimaryKeySelective(ShopTakeaway shopTakeaway); |
|||
|
|||
Page<ShopTakeaway> selectList(ShopTakeawayQuery shopTakeawayQuery); |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
package cc.hiver.mall.service.mybatis; |
|||
|
|||
import cc.hiver.mall.entity.Areas; |
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
import java.util.List; |
|||
|
|||
public interface AreasService extends IService<Areas> { |
|||
|
|||
List<Areas> listBySchoolId(String schoolId); |
|||
|
|||
Areas insert(Areas areas); |
|||
|
|||
Areas updateByAreasId(Areas areas); |
|||
|
|||
void delById(String id,Integer delFlag); |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
package cc.hiver.mall.service.mybatis; |
|||
|
|||
import cc.hiver.mall.entity.ProductGroupBuyPrice; |
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface ProductGroupBuyPriceService extends IService<ProductGroupBuyPrice> { |
|||
|
|||
ProductGroupBuyPrice insert(ProductGroupBuyPrice record); |
|||
|
|||
List<ProductGroupBuyPrice> selectByProductId(String productId); |
|||
|
|||
void deleteByProductId(String productId); |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
/* |
|||
Copyright [2022] [https://hiver.cc]
|
|||
|
|||
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. |
|||
*/ |
|||
package cc.hiver.mall.serviceimpl; |
|||
|
|||
import cc.hiver.mall.dao.mapper.ShopTakeawayMapper; |
|||
import cc.hiver.mall.entity.ShopTakeaway; |
|||
import cc.hiver.mall.pojo.query.ShopTakeawayQuery; |
|||
import cc.hiver.mall.service.ShopTakeawayService; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
|
|||
/** |
|||
* 店铺外卖业务配置服务实现类 |
|||
* |
|||
* @author cc |
|||
*/ |
|||
@Slf4j |
|||
@Service |
|||
@Transactional |
|||
public class ShopTakeawayServiceImpl extends ServiceImpl<ShopTakeawayMapper, ShopTakeaway> implements ShopTakeawayService { |
|||
|
|||
@Autowired |
|||
private ShopTakeawayMapper shopTakeawayMapper; |
|||
@Override |
|||
public ShopTakeaway selectByPrimaryKey(String shopId) { |
|||
return shopTakeawayMapper.selectByPrimaryKey(shopId); |
|||
} |
|||
|
|||
@Override |
|||
public void deleteByPrimaryKey(String shopId) { |
|||
shopTakeawayMapper.deleteByPrimaryKey(shopId); |
|||
} |
|||
|
|||
@Override |
|||
public ShopTakeaway insert(ShopTakeaway shopTakeaway) { |
|||
shopTakeawayMapper.insert(shopTakeaway); |
|||
return shopTakeaway; |
|||
} |
|||
|
|||
@Override |
|||
public void updateByPrimaryKeySelective(ShopTakeaway shopTakeaway) { |
|||
shopTakeawayMapper.updateByPrimaryKeySelective(shopTakeaway); |
|||
} |
|||
|
|||
@Override |
|||
public Page<ShopTakeaway> selectList(ShopTakeawayQuery shopTakeawayQuery) { |
|||
final Page<ShopTakeaway> page = new Page<>(shopTakeawayQuery.getPageNum(), shopTakeawayQuery.getPageSize()); |
|||
final Page<ShopTakeaway> salePage = shopTakeawayMapper.selectList(page, shopTakeawayQuery.getRegionId()); |
|||
return salePage; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
package cc.hiver.mall.serviceimpl.mybatis; |
|||
|
|||
import cc.hiver.mall.dao.mapper.AreasMapper; |
|||
import cc.hiver.mall.entity.Areas; |
|||
import cc.hiver.mall.service.mybatis.AreasService; |
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Service |
|||
public class AreasServiceImpl extends ServiceImpl<AreasMapper, Areas> implements AreasService { |
|||
|
|||
@Autowired |
|||
private AreasMapper areasMapper; |
|||
|
|||
@Override |
|||
public List<Areas> listBySchoolId(String schoolId) { |
|||
return areasMapper.listBySchoolId(schoolId); |
|||
} |
|||
|
|||
@Override |
|||
public Areas insert(Areas areas) { |
|||
areasMapper.insert(areas); |
|||
return areas; |
|||
} |
|||
|
|||
@Override |
|||
public Areas updateByAreasId(Areas areas) { |
|||
areasMapper.updateByAreasId(areas); |
|||
return areas; |
|||
} |
|||
|
|||
@Override |
|||
public void delById(String id,Integer delFlag) { |
|||
areasMapper.delById(id,delFlag); |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
package cc.hiver.mall.serviceimpl.mybatis; |
|||
|
|||
import cc.hiver.mall.dao.mapper.ProductGroupBuyPriceMapper; |
|||
import cc.hiver.mall.entity.ProductGroupBuyPrice; |
|||
import cc.hiver.mall.service.mybatis.ProductGroupBuyPriceService; |
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Service |
|||
@Slf4j |
|||
public class ProductGroupBuyPriceServiceImpl extends ServiceImpl<ProductGroupBuyPriceMapper, ProductGroupBuyPrice> implements ProductGroupBuyPriceService { |
|||
|
|||
@Autowired |
|||
private ProductGroupBuyPriceMapper productGroupBuyPriceMapper; |
|||
|
|||
|
|||
@Override |
|||
public ProductGroupBuyPrice insert(ProductGroupBuyPrice record) { |
|||
productGroupBuyPriceMapper.insert(record); |
|||
return record; |
|||
} |
|||
|
|||
@Override |
|||
public List<ProductGroupBuyPrice> selectByProductId(String productId) { |
|||
return productGroupBuyPriceMapper.selectByProductId(productId); |
|||
} |
|||
|
|||
@Override |
|||
public void deleteByProductId(String productId) { |
|||
productGroupBuyPriceMapper.deleteByProductId(productId); |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="cc.hiver.mall.dao.mapper.AreasMapper"> |
|||
<resultMap id="BaseResultMap" type="cc.hiver.mall.entity.Areas"> |
|||
<id column="id" jdbcType="VARCHAR" property="id" /> |
|||
<result column="delFlag" jdbcType="INTEGER" property="delFlag" /> |
|||
<result column="name" jdbcType="VARCHAR" property="name" /> |
|||
<result column="remark" jdbcType="VARCHAR" property="remark" /> |
|||
<result column="schoolId" jdbcType="VARCHAR" property="schoolId" /> |
|||
<result column="orderByField" jdbcType="INTEGER" property="orderByField" /> |
|||
</resultMap> |
|||
|
|||
<sql id="Base_Column_List"> |
|||
id,delFlag,name,remark,schoolId,orderByField |
|||
</sql> |
|||
|
|||
<select id="listBySchoolId" resultMap="BaseResultMap"> |
|||
select |
|||
<include refid="Base_Column_List" /> |
|||
from areas |
|||
where schoolId = #{schoolId,jdbcType=VARCHAR} order by orderByField asc |
|||
</select> |
|||
|
|||
<insert id="insert" parameterType="cc.hiver.mall.entity.Areas"> |
|||
insert into areas (id,delFlag,name,remark,schoolId,orderByField) |
|||
values (#{id,jdbcType=VARCHAR}, #{delFlag,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR} |
|||
, #{remark,jdbcType=VARCHAR} |
|||
, #{schoolId,jdbcType=VARCHAR}, #{orderByField,jdbcType=INTEGER}) |
|||
</insert> |
|||
|
|||
<update id="updateByAreasId" parameterType="cc.hiver.mall.entity.Areas"> |
|||
update areas |
|||
<set> |
|||
<if test="areas.delFlag != null"> |
|||
delFlag = #{areas.delFlag,jdbcType=INTEGER}, |
|||
</if> |
|||
<if test="areas.name != null"> |
|||
name = #{areas.name,jdbcType=VARCHAR}, |
|||
</if> |
|||
<if test="areas.remark != null"> |
|||
remark = #{areas.remark,jdbcType=VARCHAR}, |
|||
</if> |
|||
<if test="areas.schoolId != null"> |
|||
schoolId = #{areas.schoolId,jdbcType=VARCHAR}, |
|||
</if> |
|||
<if test="areas.orderByField != null"> |
|||
orderByField = #{areas.orderByField,jdbcType=INTEGER}, |
|||
</if> |
|||
</set> |
|||
where id = #{areas.id,jdbcType=VARCHAR} |
|||
</update> |
|||
|
|||
<update id="delById"> |
|||
update areas set delFlag = #{delFlag,jdbcType=INTEGER} where id = #{id,jdbcType=VARCHAR} |
|||
</update> |
|||
|
|||
</mapper> |
|||
@ -0,0 +1,22 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="cc.hiver.mall.dao.mapper.CommentMapper"> |
|||
<resultMap id="BaseResultMap" type="cc.hiver.mall.entity.Comment"> |
|||
<id column="id" jdbcType="VARCHAR" property="id"/> |
|||
<result column="linked_id" jdbcType="VARCHAR" property="linkedId"/> |
|||
<result column="remark" jdbcType="VARCHAR" property="remark"/> |
|||
<result column="level" jdbcType="INTEGER" property="level"/> |
|||
<result column="picture" jdbcType="VARCHAR" property="picture"/> |
|||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/> |
|||
<result column="create_by" jdbcType="VARCHAR" property="createBy"/> |
|||
<result column="create_by_name" jdbcType="TIMESTAMP" property="createByName"/> |
|||
<result column="create_by_icon" jdbcType="TIMESTAMP" property="createByIcon"/> |
|||
<result column="score" jdbcType="TIMESTAMP" property="score"/> |
|||
<result column="order_id" jdbcType="TIMESTAMP" property="orderId"/> |
|||
<result column="shop_id" jdbcType="TIMESTAMP" property="shopId"/> |
|||
</resultMap> |
|||
|
|||
<sql id="Base_Column_List"> |
|||
id, linked_id, remark, level, picture, create_time, create_by, create_by_name, create_by_icon, score, order_id, shop_id |
|||
</sql> |
|||
</mapper> |
|||
@ -0,0 +1,41 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="cc.hiver.mall.dao.mapper.ProductGroupBuyPriceMapper"> |
|||
<resultMap id="BaseResultMap" type="cc.hiver.mall.entity.ProductGroupBuyPrice"> |
|||
<id column="id" jdbcType="VARCHAR" property="id"/> |
|||
<result column="create_by" jdbcType="VARCHAR" property="createBy"/> |
|||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/> |
|||
<result column="del_flag" jdbcType="INTEGER" property="delFlag"/> |
|||
<result column="update_by" jdbcType="VARCHAR" property="updateBy"/> |
|||
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/> |
|||
<result column="group_count" jdbcType="INTEGER" property="groupCount"/> |
|||
<result column="product_id" jdbcType="VARCHAR" property="productId"/> |
|||
<result column="group_price" jdbcType="DECIMAL" property="groupPrice"/> |
|||
</resultMap> |
|||
|
|||
<sql id="Base_Column_List"> |
|||
id, create_by, create_time, del_flag, update_by, update_time, group_count, product_id, group_price |
|||
</sql> |
|||
|
|||
<insert id="insert" parameterType="cc.hiver.mall.entity.ProductGroupBuyPrice"> |
|||
insert into t_product_group_buy_price (id, create_by, create_time, |
|||
del_flag, update_by, update_time, |
|||
group_count, product_id, group_price) |
|||
values (#{id,jdbcType=VARCHAR}, #{createBy,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, |
|||
#{delFlag,jdbcType=INTEGER}, #{updateBy,jdbcType=VARCHAR}, #{updateTime,jdbcType=TIMESTAMP}, |
|||
#{groupCount,jdbcType=INTEGER}, #{productId,jdbcType=VARCHAR}, #{groupPrice,jdbcType=DECIMAL}) |
|||
</insert> |
|||
|
|||
<select id="selectByProductId" parameterType="java.lang.String" resultMap="BaseResultMap"> |
|||
select |
|||
<include refid="Base_Column_List"/> |
|||
from t_product_group_buy_price |
|||
where product_id = #{productId,jdbcType=VARCHAR} |
|||
</select> |
|||
|
|||
<delete id="deleteByProductId" parameterType="java.lang.String"> |
|||
delete |
|||
from t_product_group_buy_price |
|||
where product_id = #{productId,jdbcType=VARCHAR} |
|||
</delete> |
|||
</mapper> |
|||
@ -0,0 +1,105 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > |
|||
<mapper namespace="cc.hiver.mall.dao.mapper.ShopTakeawayMapper" > |
|||
<resultMap id="BaseResultMap" type="cc.hiver.mall.entity.ShopTakeaway" > |
|||
<result column="id" property="id" jdbcType="VARCHAR" /> |
|||
<result column="create_by" jdbcType="VARCHAR" property="createBy"/> |
|||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/> |
|||
<result column="del_flag" jdbcType="INTEGER" property="delFlag"/> |
|||
<result column="update_by" jdbcType="VARCHAR" property="updateBy"/> |
|||
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/> |
|||
<result column="shop_id" property="shopId" jdbcType="VARCHAR" /> |
|||
<result column="shop_name" property="shopName" jdbcType="VARCHAR" /> |
|||
<result column="region_id" property="regionId" jdbcType="VARCHAR" /> |
|||
<result column="commission_rate_more" property="commissionRateMore" jdbcType="VARCHAR" /> |
|||
<result column="commission_rate_one" property="commissionRateOne" jdbcType="VARCHAR" /> |
|||
<result column="business_hour_begin" property="businessHourBegin" jdbcType="VARCHAR" /> |
|||
<result column="business_hour_end" property="businessHourEnd" jdbcType="VARCHAR" /> |
|||
<result column="status" property="status" jdbcType="INTEGER" /> |
|||
<result column="type" property="type" jdbcType="VARCHAR" /> |
|||
<result column="is_delivery" property="isDelivery" jdbcType="INTEGER" /> |
|||
<result column="cooking_time" property="cookingTime" jdbcType="INTEGER" /> |
|||
<result column="shoprank" property="shoprank" jdbcType="INTEGER" /> |
|||
<result column="is_price_lock" property="isPriceLock" jdbcType="INTEGER" /> |
|||
<result column="join_time" property="joinTime" jdbcType="VARCHAR" /> |
|||
</resultMap> |
|||
<sql id="Base_Column_List" > |
|||
id,create_by, create_time, update_time, update_by, |
|||
del_flag,shop_id,shop_name,region_id,commission_rate_more,commission_rate_one,business_hour_begin,business_hour_end,status,type,is_delivery,cooking_time,shoprank,is_price_lock,join_time |
|||
</sql> |
|||
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" > |
|||
select |
|||
<include refid="Base_Column_List" /> |
|||
from t_shop_takeaway |
|||
where shop_id = #{shopId,jdbcType=VARCHAR} |
|||
</select> |
|||
|
|||
<select id="selectList" resultMap="BaseResultMap" parameterType="java.lang.String" > |
|||
select |
|||
<include refid="Base_Column_List" /> |
|||
from t_shop_takeaway |
|||
where region_id = #{regionId,jdbcType=VARCHAR} |
|||
</select> |
|||
|
|||
<delete id="deleteByPrimaryKey" parameterType="java.lang.String" > |
|||
delete from t_shop_takeaway |
|||
where shop_id = #{shopId,jdbcType=VARCHAR} |
|||
</delete> |
|||
<insert id="insert" parameterType="cc.hiver.mall.entity.ShopTakeaway" > |
|||
insert into t_shop_takeaway (id,create_by, create_time, update_time, update_by, |
|||
del_flag,shop_id, shop_name,region_id,commission_rate_more, commission_rate_one, business_hour_begin, business_hour_end, status, type, is_delivery, cooking_time, shoprank, is_price_lock, join_time) |
|||
values (#{id,jdbcType=VARCHAR},#{createBy,jdbcType=VARCHAR},#{createTime,jdbcType=TIMESTAMP}, |
|||
#{updateTime,jdbcType=TIMESTAMP},#{updateBy,jdbcType=VARCHAR},#{delFlag,jdbcType=INTEGER} |
|||
,#{shopId,jdbcType=VARCHAR},#{shopName,jdbcType=VARCHAR},#{regionId,jdbcType=VARCHAR}, #{commissionRateMore,jdbcType=DECIMAL}, #{commissionRateOne,jdbcType=DECIMAL}, #{businessHourBegin,jdbcType=VARCHAR}, #{businessHourEnd,jdbcType=VARCHAR}, #{status,jdbcType=INTEGER}, #{type,jdbcType=VARCHAR}, #{isDelivery,jdbcType=INTEGER}, #{cookingTime,jdbcType=INTEGER}, #{shoprank,jdbcType=INTEGER}, #{isPriceLock,jdbcType=INTEGER}, #{joinTime,jdbcType=VARCHAR}) |
|||
</insert> |
|||
|
|||
<update id="updateByPrimaryKeySelective" parameterType="cc.hiver.mall.entity.ShopTakeaway" > |
|||
update t_shop_takeaway |
|||
<set > |
|||
<if test="shopId != null" > |
|||
shop_id = #{shopId,jdbcType=VARCHAR}, |
|||
</if> |
|||
<if test="shopName != null" > |
|||
shop_name = #{shopName,jdbcType=VARCHAR}, |
|||
</if> |
|||
<if test="regionId != null" > |
|||
region_id = #{regionId,jdbcType=VARCHAR}, |
|||
</if> |
|||
<if test="commissionRateMore != null" > |
|||
commission_rate_more = #{commissionRateMore,jdbcType=DECIMAL}, |
|||
</if> |
|||
<if test="commissionRateOne != null" > |
|||
commission_rate_one = #{commissionRateOne,jdbcType=DECIMAL}, |
|||
</if> |
|||
<if test="businessHourBegin != null" > |
|||
business_hour_begin = #{businessHourBegin,jdbcType=VARCHAR}, |
|||
</if> |
|||
<if test="businessHourEnd != null" > |
|||
business_hour_end = #{businessHourEnd,jdbcType=VARCHAR}, |
|||
</if> |
|||
<if test="status != null" > |
|||
status = #{status,jdbcType=INTEGER}, |
|||
</if> |
|||
<if test="type != null" > |
|||
type = #{type,jdbcType=VARCHAR}, |
|||
</if> |
|||
<if test="isDelivery != null" > |
|||
is_delivery = #{isDelivery,jdbcType=INTEGER}, |
|||
</if> |
|||
<if test="cookingTime != null" > |
|||
cooking_time = #{cookingTime,jdbcType=INTEGER}, |
|||
</if> |
|||
<if test="shoprank != null" > |
|||
shoprank = #{shoprank,jdbcType=INTEGER}, |
|||
</if> |
|||
<if test="isPriceLock != null" > |
|||
is_price_lock = #{isPriceLock,jdbcType=INTEGER}, |
|||
</if> |
|||
<if test="joinTime != null" > |
|||
join_time = #{joinTime,jdbcType=VARCHAR}, |
|||
</if> |
|||
</set> |
|||
where shop_id = #{shopId,jdbcType=VARCHAR} |
|||
</update> |
|||
|
|||
</mapper> |
|||
@ -0,0 +1,27 @@ |
|||
package cc.hiver.social.controller; |
|||
|
|||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; |
|||
import org.bouncycastle.openssl.PEMParser; |
|||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; |
|||
|
|||
import java.io.StringReader; |
|||
import java.security.PrivateKey; |
|||
import java.security.Signature; |
|||
import java.util.Base64; |
|||
|
|||
public class KeyUtils { |
|||
|
|||
public static PrivateKey loadPrivateKey(byte[] keyBytes) throws Exception { |
|||
PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(keyBytes); |
|||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); |
|||
return converter.getPrivateKey(pkInfo); |
|||
} |
|||
|
|||
public static String signWithSha256Rsa(String message, PrivateKey privateKey) throws Exception { |
|||
Signature sign = Signature.getInstance("SHA256withRSA"); |
|||
sign.initSign(privateKey); |
|||
sign.update(message.getBytes("utf-8")); |
|||
byte[] signed = sign.sign(); |
|||
return Base64.getEncoder().encodeToString(signed); |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
package cc.hiver.social.controller; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import okhttp3.*; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
@Service |
|||
public class UnifiedOrderService { |
|||
|
|||
private static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; |
|||
|
|||
@Autowired |
|||
private WechatPayConfig config; |
|||
|
|||
@Autowired |
|||
private WechatPaySigner signer; |
|||
|
|||
private final OkHttpClient httpClient = new OkHttpClient(); |
|||
private final ObjectMapper objectMapper = new ObjectMapper(); |
|||
|
|||
public Map<String, Object> createJsapiOrder(String outTradeNo, String description, |
|||
String openid, long amount) throws Exception { |
|||
Map<String, Object> reqBody = new HashMap<>(); |
|||
reqBody.put("appid", config.getAppId()); |
|||
reqBody.put("mchid", config.getMchId()); |
|||
reqBody.put("description", description); |
|||
reqBody.put("out_trade_no", outTradeNo); |
|||
reqBody.put("notify_url", config.getNotifyUrl()); |
|||
|
|||
Map<String, Object> amountMap = new HashMap<>(); |
|||
amountMap.put("total", amount); // 单位:分
|
|||
amountMap.put("currency", "CNY"); |
|||
reqBody.put("amount", amountMap); |
|||
|
|||
Map<String, Object> payer = new HashMap<>(); |
|||
payer.put("openid", openid); |
|||
reqBody.put("payer", payer); |
|||
|
|||
String jsonBody = objectMapper.writeValueAsString(reqBody); |
|||
String authorization = signer.sign("POST", UNIFIED_ORDER_URL, jsonBody); |
|||
|
|||
Request request = new Request.Builder() |
|||
.url(UNIFIED_ORDER_URL) |
|||
.post(RequestBody.create(MediaType.get("application/json"), jsonBody)) |
|||
.addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 " + authorization) |
|||
.addHeader("Content-Type", "application/json") |
|||
.addHeader("Accept", "application/json") |
|||
.addHeader("User-Agent", "MyApp/1.0") |
|||
.build(); |
|||
|
|||
try (Response response = httpClient.newCall(request).execute()) { |
|||
if (!response.isSuccessful()) { |
|||
throw new RuntimeException("WeChat Pay API error: " + response.code() + " - " + response.body().string()); |
|||
} |
|||
String responseBody = response.body().string(); |
|||
return objectMapper.readValue(responseBody, Map.class); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
package cc.hiver.social.controller; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
@Configuration |
|||
public class WechatPayConfig { |
|||
// 小程序APPID
|
|||
@Value("${wechatpay.appid}") |
|||
private String appId; |
|||
|
|||
// 微信支付商户号
|
|||
@Value("${wechatpay.mchId}") |
|||
private String mchId; |
|||
|
|||
// 微信支付API v3密钥
|
|||
@Value("${wechatpay.apiV3Key}") |
|||
private String apiV3Key; |
|||
|
|||
// 商户API证书序列号
|
|||
@Value("${wechatpay.merchantSerialNo}") |
|||
private String serialNo; |
|||
|
|||
// 商户API私钥路径
|
|||
@Value("${wechatpay.privateKeyPath}") |
|||
private String privateKeyPath; |
|||
|
|||
// 微信支付平台证书路径
|
|||
@Value("${wechatpay.platformCertificatePath}") |
|||
private String platformCertificatePath; |
|||
|
|||
// 微信支付回调地址
|
|||
@Value("${wechatpay.notifyUrl}") |
|||
private String notifyUrl; |
|||
|
|||
// ==================== API v3 接口地址 ====================
|
|||
|
|||
// 微信支付统一下单接口 (API v3)
|
|||
public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; |
|||
|
|||
// 微信支付查询订单接口 (API v3)
|
|||
public static final String ORDER_QUERY_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s"; |
|||
|
|||
// 微信支付关闭订单接口 (API v3)
|
|||
public static final String CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s/close"; |
|||
|
|||
// 微信支付申请退款接口 (API v3)
|
|||
public static final String REFUND_URL = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; |
|||
|
|||
// 微信支付查询退款接口 (API v3)
|
|||
public static final String REFUND_QUERY_URL = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/%s"; |
|||
|
|||
// 微信支付下载对账单接口 (API v3)
|
|||
public static final String DOWNLOAD_BILL_URL = "https://api.mch.weixin.qq.com/v3/bill/tradebill"; |
|||
|
|||
// 获取平台证书列表接口 (API v3)
|
|||
public static final String GET_CERTIFICATES_URL = "https://api.mch.weixin.qq.com/v3/certificates"; |
|||
|
|||
// ==================== 常量定义 ====================
|
|||
|
|||
// 成功状态码
|
|||
public static final String SUCCESS = "SUCCESS"; |
|||
|
|||
// 失败状态码
|
|||
public static final String FAIL = "FAIL"; |
|||
|
|||
// ==================== Getter 方法 ====================
|
|||
|
|||
// 获取APPID
|
|||
public String getAppId() { |
|||
return appId; |
|||
} |
|||
|
|||
// 获取商户号
|
|||
public String getMchId() { |
|||
return mchId; |
|||
} |
|||
|
|||
// 获取API v3密钥
|
|||
public String getApiV3Key() { |
|||
return apiV3Key; |
|||
} |
|||
|
|||
// 获取商户API证书序列号
|
|||
public String getSerialNo() { |
|||
return serialNo; |
|||
} |
|||
|
|||
// 获取商户API私钥路径
|
|||
public String getPrivateKeyPath() { |
|||
return privateKeyPath; |
|||
} |
|||
|
|||
// 获取微信支付平台证书路径
|
|||
public String getPlatformCertificatePath() { |
|||
return platformCertificatePath; |
|||
} |
|||
|
|||
// 获取回调地址
|
|||
public String getNotifyUrl() { |
|||
return notifyUrl; |
|||
} |
|||
} |
|||
@ -0,0 +1,112 @@ |
|||
package cc.hiver.social.controller; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.core.io.Resource; |
|||
import org.springframework.core.io.ResourceLoader; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import java.io.ByteArrayOutputStream; |
|||
import java.io.IOException; |
|||
import java.io.InputStream; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.security.PrivateKey; |
|||
import java.util.*; |
|||
import java.util.Map; |
|||
|
|||
@RestController |
|||
@RequestMapping("/hiver/api/wechat/pay") |
|||
public class WechatPayController { |
|||
@Autowired |
|||
private UnifiedOrderService orderService; |
|||
|
|||
@Autowired |
|||
private WechatPayConfig config; |
|||
|
|||
@Autowired |
|||
private ResourceLoader resourceLoader; |
|||
|
|||
private PrivateKey privateKey; |
|||
|
|||
@PostConstruct |
|||
public void init() throws Exception { |
|||
Resource resource = resourceLoader.getResource(config.getPrivateKeyPath()); |
|||
byte[] keyBytes; |
|||
try (InputStream is = resource.getInputStream()) { |
|||
keyBytes = readAllBytes(is); |
|||
} |
|||
|
|||
String key = new String(keyBytes, StandardCharsets.UTF_8) |
|||
.replace("-----BEGIN PRIVATE KEY-----", "") |
|||
.replace("-----END PRIVATE KEY-----", "") |
|||
.replaceAll("\\s+", ""); |
|||
|
|||
byte[] decodedKey = Base64.getDecoder().decode(key); |
|||
privateKey = KeyUtils.loadPrivateKey(decodedKey); |
|||
} |
|||
|
|||
private byte[] readAllBytes(InputStream inputStream) throws IOException { |
|||
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); |
|||
byte[] data = new byte[1024]; |
|||
int nRead; |
|||
while ((nRead = inputStream.read(data, 0, data.length)) != -1) { |
|||
buffer.write(data, 0, nRead); |
|||
} |
|||
return buffer.toByteArray(); |
|||
} |
|||
|
|||
@PostMapping("/unified-order") |
|||
public ResponseEntity<Map<String, String>> jsapiPay(@RequestBody Map<String, String> request) throws Exception { |
|||
String outTradeNo = "ORDER_" + System.currentTimeMillis(); |
|||
String description = request.get("description"); |
|||
String openid = request.get("openid"); |
|||
long amount = Long.parseLong(request.get("amount")); // 单位:分
|
|||
|
|||
Map<String, Object> orderResult = orderService.createJsapiOrder(outTradeNo, description, openid, amount); |
|||
String prepayId = (String) orderResult.get("prepay_id"); |
|||
|
|||
if (prepayId == null) { |
|||
Map<String, String> payParams = new HashMap<>(); |
|||
payParams.put("code","101"); |
|||
payParams.put("message","调起微信支付失败"); |
|||
return ResponseEntity.ok(payParams); |
|||
}else{ |
|||
// 2. 生成前端支付参数
|
|||
Map<String, String> payParams = buildPaySign(prepayId); |
|||
payParams.put("code","200"); |
|||
return ResponseEntity.ok(payParams); |
|||
} |
|||
} |
|||
public Map<String, String> buildPaySign(String prepayId) throws Exception { |
|||
// 1. 生成参数
|
|||
String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); // 秒
|
|||
String nonceStr = UUID.randomUUID().toString().replace("-", "").substring(0, 32); |
|||
String packageValue = "prepay_id=" + prepayId; |
|||
String signType = "RSA"; |
|||
|
|||
// 2. 构建签名字符串
|
|||
String message = String.join("\n", |
|||
config.getAppId(), |
|||
timeStamp, |
|||
nonceStr, |
|||
packageValue, |
|||
"" // 最后一个 \n
|
|||
); |
|||
|
|||
// 3. 使用商户私钥签名(和 API v3 同一套私钥)
|
|||
String paySign = KeyUtils.signWithSha256Rsa(message, privateKey); // ← 复用之前的签名方法
|
|||
|
|||
// 4. 返回给前端
|
|||
Map<String, String> result = new HashMap<>(); |
|||
result.put("timeStamp", timeStamp); |
|||
result.put("nonceStr", nonceStr); |
|||
result.put("package", packageValue); |
|||
result.put("signType", signType); |
|||
result.put("paySign", paySign); |
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
package cc.hiver.social.controller; |
|||
|
|||
import com.alipay.api.internal.util.file.IOUtils; |
|||
import okhttp3.HttpUrl; |
|||
import org.bouncycastle.jce.provider.BouncyCastleProvider; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.core.io.Resource; |
|||
import org.springframework.core.io.ResourceLoader; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import java.io.ByteArrayOutputStream; |
|||
import java.io.IOException; |
|||
import java.io.InputStream; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.security.PrivateKey; |
|||
import java.security.Security; |
|||
import java.security.cert.X509Certificate; |
|||
import java.util.Base64; |
|||
import java.util.UUID; |
|||
|
|||
@Component |
|||
public class WechatPaySigner { |
|||
|
|||
static { |
|||
Security.addProvider(new BouncyCastleProvider()); |
|||
} |
|||
|
|||
@Autowired |
|||
private WechatPayConfig config; |
|||
|
|||
@Autowired |
|||
private ResourceLoader resourceLoader; |
|||
|
|||
private PrivateKey privateKey; |
|||
|
|||
@PostConstruct |
|||
public void init() throws Exception { |
|||
Resource resource = resourceLoader.getResource(config.getPrivateKeyPath()); |
|||
byte[] keyBytes; |
|||
try (InputStream is = resource.getInputStream()) { |
|||
keyBytes = readAllBytes(is); |
|||
} |
|||
|
|||
String key = new String(keyBytes, StandardCharsets.UTF_8) |
|||
.replace("-----BEGIN PRIVATE KEY-----", "") |
|||
.replace("-----END PRIVATE KEY-----", "") |
|||
.replaceAll("\\s+", ""); |
|||
|
|||
byte[] decodedKey = Base64.getDecoder().decode(key); |
|||
privateKey = KeyUtils.loadPrivateKey(decodedKey); |
|||
} |
|||
|
|||
// 辅助方法:Java 8 兼容的 readAllBytes
|
|||
private byte[] readAllBytes(InputStream inputStream) throws IOException { |
|||
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); |
|||
byte[] data = new byte[1024]; |
|||
int nRead; |
|||
while ((nRead = inputStream.read(data, 0, data.length)) != -1) { |
|||
buffer.write(data, 0, nRead); |
|||
} |
|||
return buffer.toByteArray(); |
|||
} |
|||
|
|||
public String sign(String method, String url, String body) throws Exception { |
|||
// 解析 URL 获取 path 和 query
|
|||
HttpUrl httpUrl = HttpUrl.parse(url); |
|||
if (httpUrl == null) { |
|||
throw new IllegalArgumentException("Invalid URL: " + url); |
|||
} |
|||
|
|||
String path = httpUrl.encodedPath(); // 如 "/v3/pay/transactions/jsapi"
|
|||
String query = httpUrl.query(); // 如 "a=1&b=2",若无则为 null
|
|||
|
|||
String nonceStr = UUID.randomUUID().toString().replace("-", "").substring(0, 32); |
|||
long timestamp = System.currentTimeMillis() / 1000; // 秒!
|
|||
|
|||
// 构建签名消息(严格按格式)
|
|||
StringBuilder sb = new StringBuilder(); |
|||
sb.append(method).append("\n") |
|||
.append(path).append("\n") |
|||
.append(timestamp).append("\n") |
|||
.append(nonceStr).append("\n") |
|||
.append(body == null ? "" : body).append("\n"); |
|||
|
|||
String message = sb.toString(); |
|||
|
|||
// 打印用于调试(上线前删除)
|
|||
System.out.println("Sign message:\n" + message); |
|||
|
|||
String signature = KeyUtils.signWithSha256Rsa(message, privateKey); |
|||
|
|||
return String.format( |
|||
"mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"", |
|||
config.getMchId(), nonceStr, timestamp, config.getSerialNo(), signature |
|||
); |
|||
} |
|||
|
|||
private String buildMessage(String method, String path, String query, String body, |
|||
String nonceStr, long timestamp) { |
|||
StringBuilder sb = new StringBuilder(); |
|||
sb.append(method).append("\n") |
|||
.append(path).append("\n") |
|||
.append(query == null ? "" : query).append("\n") |
|||
.append(timestamp).append("\n") |
|||
.append(nonceStr).append("\n") |
|||
.append(body == null ? "" : body).append("\n"); |
|||
return sb.toString(); |
|||
} |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
package cc.hiver.social.serviceimpl; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.web.client.RestTemplate; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
@Service |
|||
public class WechatPhoneService { |
|||
|
|||
private static final Logger log = LoggerFactory.getLogger(WechatPhoneService.class); |
|||
|
|||
// 微信获取手机号接口地址 (新版接口)
|
|||
private static final String GET_PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={accessToken}"; |
|||
|
|||
// 微信获取 Access Token 接口地址
|
|||
private static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}"; |
|||
|
|||
private final RestTemplate restTemplate; |
|||
private final ObjectMapper objectMapper; |
|||
|
|||
// 请替换为你的实际配置,建议放入 application.yml
|
|||
@Value("${hiver.social.wechat.appId}") |
|||
private String appId; |
|||
|
|||
@Value("${hiver.social.wechat.appSecret}") |
|||
private String appSecret; |
|||
|
|||
public WechatPhoneService() { |
|||
this.restTemplate = new RestTemplate(); |
|||
this.objectMapper = new ObjectMapper(); |
|||
} |
|||
|
|||
/** |
|||
* 获取手机号核心方法 |
|||
* @param code 前端传来的 code (注意:不是登录code,是 getPhoneNumber 返回的 code) |
|||
* @return 手机号字符串,失败返回 null |
|||
*/ |
|||
public String getPhoneNumber(String code) { |
|||
try { |
|||
// 1. 获取 Access Token (生产环境建议缓存此 Token,有效期2小时)
|
|||
String accessToken = getAccessToken(); |
|||
if (accessToken == null) { |
|||
log.error("获取 AccessToken 失败"); |
|||
return null; |
|||
} |
|||
|
|||
// 2. 构建请求参数
|
|||
Map<String, Object> params = new HashMap<>(); |
|||
params.put("code", code); |
|||
|
|||
// 3. 调用微信接口
|
|||
ResponseEntity<String> response = restTemplate.postForEntity( |
|||
GET_PHONE_NUMBER_URL, |
|||
params, |
|||
String.class, |
|||
accessToken |
|||
); |
|||
|
|||
// 4. 解析响应
|
|||
JsonNode root = objectMapper.readTree(response.getBody()); |
|||
|
|||
// 检查错误码
|
|||
int errcode = root.path("errcode").asInt(-1); |
|||
if (errcode != 0) { |
|||
String errmsg = root.path("errmsg").asText("未知错误"); |
|||
log.error("微信手机号获取失败: errcode={}, errmsg={}", errcode, errmsg); |
|||
return null; |
|||
} |
|||
|
|||
// 5. 提取手机号
|
|||
// 新版接口返回结构: { "phone_info": { "phoneNumber": "138...", ... } }
|
|||
JsonNode phoneInfo = root.path("phone_info"); |
|||
if (phoneInfo.isMissingNode()) { |
|||
// 兼容旧版接口直接返回 phone_number 的情况 (极少见,主要是为了容错)
|
|||
return root.path("phone_number").asText(null); |
|||
} |
|||
|
|||
return phoneInfo.path("phoneNumber").asText(null); |
|||
|
|||
} catch (Exception e) { |
|||
log.error("解析手机号异常", e); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取 Access Token (简易版,生产环境请务必加 Redis 缓存) |
|||
*/ |
|||
public String getAccessToken() { |
|||
if (appId == null || appSecret == null) { |
|||
throw new IllegalStateException("配置未加载: appId=" + appId + ", appSecret=" + (appSecret != null)); |
|||
} |
|||
try { |
|||
ResponseEntity<String> response = restTemplate.getForEntity( |
|||
GET_ACCESS_TOKEN_URL, |
|||
String.class, |
|||
appId, // 对应 {appid}
|
|||
appSecret // 对应 {secret}
|
|||
); |
|||
|
|||
JsonNode root = objectMapper.readTree(response.getBody()); |
|||
if (root.has("access_token")) { |
|||
return root.path("access_token").asText(); |
|||
} else { |
|||
log.error("获取 Token 失败: {}", response.getBody()); |
|||
return null; |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("获取 AccessToken 异常", e); |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> |
|||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8"> |
|||
<output url="file://$MODULE_DIR$/target/classes" /> |
|||
<output-test url="file://$MODULE_DIR$/target/test-classes" /> |
|||
<content url="file://$MODULE_DIR$"> |
|||
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/annotations" isTestSource="false" generated="true" /> |
|||
<excludeFolder url="file://$MODULE_DIR$/target" /> |
|||
</content> |
|||
<orderEntry type="inheritedJdk" /> |
|||
<orderEntry type="sourceFolder" forTests="false" /> |
|||
</component> |
|||
</module> |
|||
Loading…
Reference in new issue