Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
691af29b
authored
Jun 04, 2026
by
renyizhao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
充值功能完成
parent
7bc08650
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
746 additions
and
3 deletions
+746
-3
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/config/NewApiProperties.java
+14
-0
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/admin/newapi/AiTokenRespVO.java
+2
-0
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/app/AppAiTokenController.java
+31
-2
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/service/newapi/NewApiClient.java
+50
-0
computility-module-member/src/main/java/com/luhu/computility/module/member/config/MemberNewApiClient.java
+396
-0
computility-module-member/src/main/java/com/luhu/computility/module/member/config/MemberNewApiProperties.java
+48
-0
computility-module-member/src/main/java/com/luhu/computility/module/member/config/MemberNewApiResponse.java
+23
-0
computility-module-member/src/main/java/com/luhu/computility/module/member/controller/admin/notify/MemberRechargeWpgjPayController.java
+9
-0
computility-module-member/src/main/java/com/luhu/computility/module/member/controller/admin/recharge/vo/MemberRechargeRespVO.java
+13
-0
computility-module-member/src/main/java/com/luhu/computility/module/member/dal/dataobject/recharge/MemberRechargeDO.java
+15
-0
computility-module-member/src/main/java/com/luhu/computility/module/member/service/recharge/MemberRechargeService.java
+6
-0
computility-module-member/src/main/java/com/luhu/computility/module/member/service/recharge/MemberRechargeServiceImpl.java
+136
-0
computility-server/src/main/resources/application-local.yaml
+3
-1
No files found.
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/config/NewApiProperties.java
View file @
691af29b
...
...
@@ -9,6 +9,11 @@ public class NewApiProperties {
private
String
adminToken
;
/**
* New API 用户ID(管理员)
*/
private
String
adminUserId
=
"1"
;
public
String
getBaseUrl
()
{
return
baseUrl
;
}
...
...
@@ -24,4 +29,12 @@ public class NewApiProperties {
public
void
setAdminToken
(
String
adminToken
)
{
this
.
adminToken
=
adminToken
;
}
public
String
getAdminUserId
()
{
return
adminUserId
;
}
public
void
setAdminUserId
(
String
adminUserId
)
{
this
.
adminUserId
=
adminUserId
;
}
}
\ No newline at end of file
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/admin/newapi/AiTokenRespVO.java
View file @
691af29b
...
...
@@ -14,4 +14,5 @@ public class AiTokenRespVO {
private
Boolean
hasToken
;
private
String
apiKey
;
private
String
message
;
private
String
balance
;
}
\ No newline at end of file
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/app/AppAiTokenController.java
View file @
691af29b
...
...
@@ -2,13 +2,16 @@ package com.luhu.computility.module.apihub.controller.app;
import
com.luhu.computility.framework.security.core.util.SecurityFrameworkUtils
;
import
com.luhu.computility.module.apihub.controller.admin.newapi.AiTokenRespVO
;
import
com.luhu.computility.module.apihub.controller.admin.newapi.NewApiResponse
;
import
com.luhu.computility.module.apihub.service.newapi.AiTokenService
;
import
com.luhu.computility.module.apihub.service.newapi.NewApiClient
;
import
com.luhu.computility.module.member.dal.dataobject.user.MemberUserDO
;
import
com.luhu.computility.module.member.service.user.MemberUserService
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.web.bind.annotation.*
;
import
javax.annotation.Resource
;
import
java.util.Map
;
@Slf4j
@RestController
...
...
@@ -21,6 +24,9 @@ public class AppAiTokenController {
@Resource
private
MemberUserService
memberUserService
;
@Resource
private
NewApiClient
newApiClient
;
@PostMapping
(
"/get"
)
public
AiTokenRespVO
getToken
()
{
Long
userId
=
SecurityFrameworkUtils
.
getLoginUserId
();
...
...
@@ -90,11 +96,33 @@ public class AppAiTokenController {
boolean
hasToken
=
user
.
getNewapiKey
()
!=
null
&&
!
user
.
getNewapiKey
().
isEmpty
();
log
.
info
(
"[查询令牌] hasToken={}, apiKey={}"
,
hasToken
,
hasToken
?
"***"
:
null
);
return
AiTokenRespVO
.
builder
()
// 获取 New API 真实余额
String
balance
=
"0.00"
;
if
(
hasToken
&&
user
.
getNewapiUserId
()
!=
null
&&
user
.
getNewapiAccessToken
()
!=
null
)
{
try
{
Integer
newapiUserId
=
Integer
.
parseInt
(
user
.
getNewapiUserId
().
toString
());
NewApiResponse
<
Map
<
String
,
Object
>>
infoResp
=
newApiClient
.
getUserInfo
(
user
.
getNewapiAccessToken
(),
newapiUserId
);
if
(
infoResp
.
getSuccess
()
&&
infoResp
.
getData
()
!=
null
)
{
Map
<
String
,
Object
>
data
=
infoResp
.
getData
();
Long
quota
=
data
.
get
(
"quota"
)
!=
null
?
((
Number
)
data
.
get
(
"quota"
)).
longValue
()
:
0L
;
// 1元 = 500000 quota
double
yuan
=
quota
/
500000.0
;
balance
=
String
.
format
(
"%.2f"
,
yuan
);
log
.
info
(
"[查询令牌] New API 余额: quota={}, yuan={}"
,
quota
,
balance
);
}
}
catch
(
Exception
e
)
{
log
.
error
(
"[查询令牌] 获取 New API 余额失败"
,
e
);
}
}
AiTokenRespVO
.
AiTokenRespVOBuilder
builder
=
AiTokenRespVO
.
builder
()
.
success
(
true
)
.
hasToken
(
hasToken
)
.
apiKey
(
user
.
getNewapiKey
())
.
build
();
.
balance
(
balance
);
return
builder
.
build
();
}
}
\ No newline at end of file
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/service/newapi/NewApiClient.java
View file @
691af29b
...
...
@@ -233,6 +233,56 @@ public class NewApiClient {
}
}
/**
* 获取用户信息(包含 quota 余额)
*/
public
NewApiResponse
<
Map
<
String
,
Object
>>
getUserInfo
(
String
accessToken
,
Integer
userId
)
{
String
url
=
newApiProperties
.
getBaseUrl
()
+
"/api/user/self"
;
log
.
info
(
"[NewApiClient.getUserInfo] 请求URL: {}"
,
url
);
try
{
HttpResponse
response
=
HttpRequest
.
get
(
url
)
.
header
(
HEADER_USER
,
String
.
valueOf
(
userId
))
.
header
(
HEADER_AUTH
,
"Bearer "
+
accessToken
)
.
timeout
(
30000
)
.
execute
();
log
.
info
(
"[NewApiClient.getUserInfo] 响应状态: {}"
,
response
.
getStatus
());
log
.
info
(
"[NewApiClient.getUserInfo] 响应内容: {}"
,
response
.
body
());
String
body
=
response
.
body
();
if
(
StrUtil
.
isBlank
(
body
))
{
return
NewApiResponse
.<
Map
<
String
,
Object
>>
builder
()
.
success
(
false
)
.
message
(
"Empty response"
)
.
httpResponse
(
response
)
.
build
();
}
JSONObject
json
=
JSONUtil
.
parseObj
(
body
);
NewApiResponse
<
Map
<
String
,
Object
>>
result
=
new
NewApiResponse
<>();
result
.
setSuccess
(
json
.
getBool
(
"success"
));
result
.
setMessage
(
json
.
getStr
(
"message"
));
result
.
setHttpResponse
(
response
);
if
(
json
.
containsKey
(
"data"
)
&&
!
json
.
isNull
(
"data"
))
{
Object
data
=
json
.
get
(
"data"
);
if
(
data
instanceof
Map
)
{
result
.
setData
((
Map
<
String
,
Object
>)
data
);
}
else
{
result
.
setData
((
Map
<
String
,
Object
>)
JSONUtil
.
parseObj
(
data
.
toString
()));
}
}
return
result
;
}
catch
(
Exception
e
)
{
log
.
error
(
"[NewApiClient.getUserInfo] 请求异常"
,
e
);
return
NewApiResponse
.<
Map
<
String
,
Object
>>
builder
()
.
success
(
false
)
.
message
(
"请求异常: "
+
e
.
getMessage
())
.
build
();
}
}
@SuppressWarnings
(
"unchecked"
)
private
<
T
>
NewApiResponse
<
T
>
parseResponse
(
HttpResponse
response
,
Class
<
T
>
dataClass
)
{
try
{
...
...
computility-module-member/src/main/java/com/luhu/computility/module/member/config/MemberNewApiClient.java
0 → 100644
View file @
691af29b
package
com
.
luhu
.
computility
.
module
.
member
.
config
;
import
cn.hutool.core.util.StrUtil
;
import
cn.hutool.http.HttpRequest
;
import
cn.hutool.http.HttpResponse
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Qualifier
;
import
org.springframework.stereotype.Component
;
import
javax.annotation.Resource
;
import
java.math.BigDecimal
;
import
java.util.HashMap
;
import
java.util.Map
;
/**
* New API HTTP 客户端(会员模块专用)
*/
@Slf4j
@Component
(
"memberNewApiClient"
)
public
class
MemberNewApiClient
{
@Resource
@Qualifier
(
"memberNewApiProperties"
)
private
MemberNewApiProperties
newApiProperties
;
private
static
final
String
HEADER_USER
=
"New-Api-User"
;
private
static
final
String
HEADER_AUTH
=
"Authorization"
;
private
static
final
String
CONTENT_TYPE
=
"application/json"
;
/**
* 费用换算比例:1元 = 500000 quota
*/
private
static
final
long
QUOTA_RATIO
=
500000L
;
/**
* 将金额转换为 quota
*/
public
long
convertToQuota
(
BigDecimal
amount
)
{
return
amount
.
multiply
(
new
BigDecimal
(
QUOTA_RATIO
)).
longValue
();
}
/**
* 创建兑换码(管理员权限)
*
* @param adminToken 管理员令牌
* @param quota 兑换额度(new api quota)
* @param amount 兑换金额(元)
* @param count 数量
* @param name 名称
* @return 兑换码(返回第一个)
*/
public
MemberNewApiResponse
<
String
>
createRedemption
(
String
adminToken
,
Long
quota
,
BigDecimal
amount
,
Integer
count
,
String
name
)
{
String
url
=
newApiProperties
.
getBaseUrl
()
+
"/api/redemption/"
;
log
.
info
(
"[MemberNewApiClient.createRedemption] 请求URL: {}"
,
url
);
log
.
info
(
"[MemberNewApiClient.createRedemption] 请求参数: quota={}, amount={}, count={}, name={}"
,
quota
,
amount
,
count
,
name
);
Map
<
String
,
Object
>
body
=
new
HashMap
<>();
body
.
put
(
"quota"
,
quota
);
body
.
put
(
"amount"
,
amount
);
body
.
put
(
"count"
,
count
);
body
.
put
(
"expired_time"
,
0
);
body
.
put
(
"name"
,
name
);
try
{
HttpResponse
response
=
HttpRequest
.
post
(
url
)
.
header
(
HEADER_USER
,
newApiProperties
.
getAdminUserId
())
.
header
(
HEADER_AUTH
,
"Bearer "
+
adminToken
)
.
body
(
JSONUtil
.
toJsonStr
(
body
))
.
contentType
(
CONTENT_TYPE
)
.
timeout
(
30000
)
.
execute
();
log
.
info
(
"[MemberNewApiClient.createRedemption] 响应状态: {}"
,
response
.
getStatus
());
log
.
info
(
"[MemberNewApiClient.createRedemption] 响应内容: {}"
,
response
.
body
());
// New API 返回 data 是一个数组,需要取出第一个元素
return
parseRedemptionCodeResponse
(
response
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[MemberNewApiClient.createRedemption] 请求异常"
,
e
);
return
MemberNewApiResponse
.<
String
>
builder
()
.
success
(
false
)
.
message
(
"请求异常: "
+
e
.
getMessage
())
.
build
();
}
}
/**
* 使用兑换码(用户权限)
*
* @param accessToken 用户访问令牌
* @param userId New API 用户ID
* @param code 兑换码
* @return 兑换后的额度
*/
public
MemberNewApiResponse
<
Long
>
useRedemption
(
String
accessToken
,
Integer
userId
,
String
code
)
{
String
url
=
newApiProperties
.
getBaseUrl
()
+
"/api/user/topup"
;
log
.
info
(
"[MemberNewApiClient.useRedemption] 请求URL: {}"
,
url
);
log
.
info
(
"[MemberNewApiClient.useRedemption] 请求参数: userId={}, code={}"
,
userId
,
code
);
Map
<
String
,
Object
>
body
=
new
HashMap
<>();
body
.
put
(
"key"
,
code
);
try
{
HttpResponse
response
=
HttpRequest
.
post
(
url
)
.
header
(
HEADER_USER
,
String
.
valueOf
(
userId
))
.
header
(
HEADER_AUTH
,
"Bearer "
+
accessToken
)
.
body
(
JSONUtil
.
toJsonStr
(
body
))
.
contentType
(
CONTENT_TYPE
)
.
timeout
(
30000
)
.
execute
();
log
.
info
(
"[MemberNewApiClient.useRedemption] 响应状态: {}"
,
response
.
getStatus
());
log
.
info
(
"[MemberNewApiClient.useRedemption] 响应内容: {}"
,
response
.
body
());
return
parseLongResponse
(
response
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[MemberNewApiClient.useRedemption] 请求异常"
,
e
);
return
MemberNewApiResponse
.<
Long
>
builder
()
.
success
(
false
)
.
message
(
"请求异常: "
+
e
.
getMessage
())
.
build
();
}
}
/**
* 获取用户额度
*
* @param accessToken 用户访问令牌
* @param userId New API 用户ID
* @return 用户信息中的 quota
*/
public
MemberNewApiResponse
<
Long
>
getUserQuota
(
String
accessToken
,
Integer
userId
)
{
String
url
=
newApiProperties
.
getBaseUrl
()
+
"/api/user/self"
;
log
.
info
(
"[MemberNewApiClient.getUserQuota] 请求URL: {}"
,
url
);
log
.
info
(
"[MemberNewApiClient.getUserQuota] userId={}"
,
userId
);
try
{
HttpResponse
response
=
HttpRequest
.
get
(
url
)
.
header
(
HEADER_USER
,
String
.
valueOf
(
userId
))
.
header
(
HEADER_AUTH
,
"Bearer "
+
accessToken
)
.
timeout
(
30000
)
.
execute
();
log
.
info
(
"[MemberNewApiClient.getUserQuota] 响应状态: {}"
,
response
.
getStatus
());
log
.
info
(
"[MemberNewApiClient.getUserQuota] 响应内容: {}"
,
response
.
body
());
String
body
=
response
.
body
();
if
(
StrUtil
.
isBlank
(
body
))
{
return
MemberNewApiResponse
.<
Long
>
builder
()
.
success
(
false
)
.
message
(
"Empty response"
)
.
build
();
}
JSONObject
json
=
JSONUtil
.
parseObj
(
body
);
MemberNewApiResponse
<
Long
>
result
=
MemberNewApiResponse
.<
Long
>
builder
()
.
success
(
json
.
getBool
(
"success"
))
.
message
(
json
.
getStr
(
"message"
))
.
build
();
if
(
json
.
containsKey
(
"data"
)
&&
!
json
.
isNull
(
"data"
))
{
JSONObject
data
=
json
.
getJSONObject
(
"data"
);
Long
quota
=
data
.
getLong
(
"quota"
);
result
.
setData
(
quota
);
}
return
result
;
}
catch
(
Exception
e
)
{
log
.
error
(
"[MemberNewApiClient.getUserQuota] 请求异常"
,
e
);
return
MemberNewApiResponse
.<
Long
>
builder
()
.
success
(
false
)
.
message
(
"请求异常: "
+
e
.
getMessage
())
.
build
();
}
}
/**
* 用户登录获取令牌
*
* @param username 用户名
* @param password 密码
* @return 登录响应(包含 accessToken)
*/
public
MemberNewApiResponse
<
Map
<
String
,
Object
>>
login
(
String
username
,
String
password
)
{
String
url
=
newApiProperties
.
getBaseUrl
()
+
"/api/user/login"
;
log
.
info
(
"[MemberNewApiClient.login] 请求URL: {}"
,
url
);
log
.
info
(
"[MemberNewApiClient.login] 请求参数: username={}"
,
username
);
Map
<
String
,
Object
>
body
=
new
HashMap
<>();
body
.
put
(
"username"
,
username
);
body
.
put
(
"password"
,
password
);
try
{
HttpResponse
response
=
HttpRequest
.
post
(
url
)
.
header
(
HEADER_USER
,
newApiProperties
.
getAdminUserId
())
.
body
(
JSONUtil
.
toJsonStr
(
body
))
.
contentType
(
CONTENT_TYPE
)
.
timeout
(
30000
)
.
execute
();
log
.
info
(
"[MemberNewApiClient.login] 响应状态: {}"
,
response
.
getStatus
());
log
.
info
(
"[MemberNewApiClient.login] 响应内容: {}"
,
response
.
body
());
return
parseMapResponse
(
response
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[MemberNewApiClient.login] 请求异常"
,
e
);
return
MemberNewApiResponse
.<
Map
<
String
,
Object
>>
builder
()
.
success
(
false
)
.
message
(
"请求异常: "
+
e
.
getMessage
())
.
build
();
}
}
/**
* 生成访问令牌
*
* @param accessToken 现有访问令牌
* @param userId 用户ID
* @return 新生成的访问令牌
*/
public
MemberNewApiResponse
<
String
>
generateAccessToken
(
String
accessToken
,
Integer
userId
)
{
String
url
=
newApiProperties
.
getBaseUrl
()
+
"/api/user/token"
;
log
.
info
(
"[MemberNewApiClient.generateAccessToken] 请求URL: {}"
,
url
);
log
.
info
(
"[MemberNewApiClient.generateAccessToken] userId={}"
,
userId
);
try
{
HttpResponse
response
=
HttpRequest
.
get
(
url
)
.
header
(
HEADER_USER
,
String
.
valueOf
(
userId
))
.
header
(
HEADER_AUTH
,
"Bearer "
+
accessToken
)
.
timeout
(
30000
)
.
execute
();
log
.
info
(
"[MemberNewApiClient.generateAccessToken] 响应状态: {}"
,
response
.
getStatus
());
log
.
info
(
"[MemberNewApiClient.generateAccessToken] 响应内容: {}"
,
response
.
body
());
return
parseResponse
(
response
,
String
.
class
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[MemberNewApiClient.generateAccessToken] 请求异常"
,
e
);
return
MemberNewApiResponse
.<
String
>
builder
()
.
success
(
false
)
.
message
(
"请求异常: "
+
e
.
getMessage
())
.
build
();
}
}
/**
* 解析兑换码创建响应(data 是数组,取第一个元素)
*/
private
MemberNewApiResponse
<
String
>
parseRedemptionCodeResponse
(
HttpResponse
response
)
{
try
{
String
body
=
response
.
body
();
if
(
StrUtil
.
isBlank
(
body
))
{
return
MemberNewApiResponse
.<
String
>
builder
()
.
success
(
false
)
.
message
(
"Empty response"
)
.
build
();
}
JSONObject
json
=
JSONUtil
.
parseObj
(
body
);
MemberNewApiResponse
<
String
>
result
=
MemberNewApiResponse
.<
String
>
builder
()
.
success
(
json
.
getBool
(
"success"
))
.
message
(
json
.
getStr
(
"message"
))
.
build
();
if
(
json
.
containsKey
(
"data"
)
&&
!
json
.
isNull
(
"data"
))
{
Object
data
=
json
.
get
(
"data"
);
// data 是一个字符串数组,取第一个
if
(
data
instanceof
cn
.
hutool
.
json
.
JSONArray
)
{
cn
.
hutool
.
json
.
JSONArray
arr
=
(
cn
.
hutool
.
json
.
JSONArray
)
data
;
if
(!
arr
.
isEmpty
())
{
result
.
setData
(
arr
.
getStr
(
0
));
}
else
{
result
.
setSuccess
(
false
);
result
.
setMessage
(
"兑换码列表为空"
);
}
}
else
if
(
data
instanceof
String
)
{
result
.
setData
((
String
)
data
);
}
}
return
result
;
}
catch
(
Exception
e
)
{
log
.
error
(
"[MemberNewApiClient.parseRedemptionCodeResponse] 解析响应异常"
,
e
);
return
MemberNewApiResponse
.<
String
>
builder
()
.
success
(
false
)
.
message
(
"解析响应失败: "
+
e
.
getMessage
())
.
build
();
}
}
@SuppressWarnings
(
"unchecked"
)
private
<
T
>
MemberNewApiResponse
<
T
>
parseResponse
(
HttpResponse
response
,
Class
<
T
>
dataClass
)
{
try
{
String
body
=
response
.
body
();
if
(
StrUtil
.
isBlank
(
body
))
{
return
MemberNewApiResponse
.<
T
>
builder
()
.
success
(
false
)
.
message
(
"Empty response"
)
.
build
();
}
JSONObject
json
=
JSONUtil
.
parseObj
(
body
);
MemberNewApiResponse
<
T
>
result
=
MemberNewApiResponse
.<
T
>
builder
()
.
success
(
json
.
getBool
(
"success"
))
.
message
(
json
.
getStr
(
"message"
))
.
build
();
if
(
json
.
containsKey
(
"data"
)
&&
!
json
.
isNull
(
"data"
)
&&
dataClass
!=
null
)
{
Object
data
=
json
.
get
(
"data"
);
if
(
dataClass
==
String
.
class
)
{
if
(
data
instanceof
String
)
{
result
.
setData
((
T
)
data
);
}
else
{
result
.
setData
((
T
)
data
.
toString
());
}
}
else
if
(
dataClass
==
Void
.
class
)
{
result
.
setData
(
null
);
}
else
if
(
data
instanceof
JSONObject
)
{
result
.
setData
((
T
)
JSONUtil
.
toBean
((
JSONObject
)
data
,
dataClass
));
}
else
{
result
.
setData
((
T
)
data
);
}
}
return
result
;
}
catch
(
Exception
e
)
{
log
.
error
(
"[MemberNewApiClient.parseResponse] 解析响应异常"
,
e
);
return
MemberNewApiResponse
.<
T
>
builder
()
.
success
(
false
)
.
message
(
"解析响应失败: "
+
e
.
getMessage
())
.
build
();
}
}
private
MemberNewApiResponse
<
Long
>
parseLongResponse
(
HttpResponse
response
)
{
try
{
String
body
=
response
.
body
();
if
(
StrUtil
.
isBlank
(
body
))
{
return
MemberNewApiResponse
.<
Long
>
builder
()
.
success
(
false
)
.
message
(
"Empty response"
)
.
build
();
}
JSONObject
json
=
JSONUtil
.
parseObj
(
body
);
MemberNewApiResponse
<
Long
>
result
=
MemberNewApiResponse
.<
Long
>
builder
()
.
success
(
json
.
getBool
(
"success"
))
.
message
(
json
.
getStr
(
"message"
))
.
build
();
if
(
json
.
containsKey
(
"data"
)
&&
!
json
.
isNull
(
"data"
))
{
Object
data
=
json
.
get
(
"data"
);
if
(
data
instanceof
Number
)
{
result
.
setData
(((
Number
)
data
).
longValue
());
}
else
{
result
.
setData
(
Long
.
parseLong
(
data
.
toString
()));
}
}
return
result
;
}
catch
(
Exception
e
)
{
log
.
error
(
"[MemberNewApiClient.parseLongResponse] 解析响应异常"
,
e
);
return
MemberNewApiResponse
.<
Long
>
builder
()
.
success
(
false
)
.
message
(
"解析响应失败: "
+
e
.
getMessage
())
.
build
();
}
}
@SuppressWarnings
(
"unchecked"
)
private
MemberNewApiResponse
<
Map
<
String
,
Object
>>
parseMapResponse
(
HttpResponse
response
)
{
try
{
String
body
=
response
.
body
();
if
(
StrUtil
.
isBlank
(
body
))
{
return
MemberNewApiResponse
.<
Map
<
String
,
Object
>>
builder
()
.
success
(
false
)
.
message
(
"Empty response"
)
.
build
();
}
JSONObject
json
=
JSONUtil
.
parseObj
(
body
);
MemberNewApiResponse
<
Map
<
String
,
Object
>>
result
=
MemberNewApiResponse
.<
Map
<
String
,
Object
>>
builder
()
.
success
(
json
.
getBool
(
"success"
))
.
message
(
json
.
getStr
(
"message"
))
.
build
();
if
(
json
.
containsKey
(
"data"
)
&&
!
json
.
isNull
(
"data"
))
{
Object
data
=
json
.
get
(
"data"
);
if
(
data
instanceof
JSONObject
)
{
result
.
setData
((
Map
<
String
,
Object
>)
data
);
}
else
{
result
.
setData
((
Map
<
String
,
Object
>)
JSONUtil
.
parseObj
(
data
.
toString
()));
}
}
return
result
;
}
catch
(
Exception
e
)
{
log
.
error
(
"[MemberNewApiClient.parseMapResponse] 解析响应异常"
,
e
);
return
MemberNewApiResponse
.<
Map
<
String
,
Object
>>
builder
()
.
success
(
false
)
.
message
(
"解析响应失败: "
+
e
.
getMessage
())
.
build
();
}
}
}
computility-module-member/src/main/java/com/luhu/computility/module/member/config/MemberNewApiProperties.java
0 → 100644
View file @
691af29b
package
com
.
luhu
.
computility
.
module
.
member
.
config
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
import
org.springframework.stereotype.Component
;
/**
* New API 配置(会员模块专用)
*/
@Component
(
"memberNewApiProperties"
)
@ConfigurationProperties
(
prefix
=
"computility.new-api"
)
public
class
MemberNewApiProperties
{
private
String
baseUrl
=
"http://localhost:3000"
;
/**
* 管理员访问令牌
*/
private
String
adminToken
;
/**
* New API 用户ID(管理员)
*/
private
String
adminUserId
=
"1"
;
public
String
getBaseUrl
()
{
return
baseUrl
;
}
public
void
setBaseUrl
(
String
baseUrl
)
{
this
.
baseUrl
=
baseUrl
;
}
public
String
getAdminToken
()
{
return
adminToken
;
}
public
void
setAdminToken
(
String
adminToken
)
{
this
.
adminToken
=
adminToken
;
}
public
String
getAdminUserId
()
{
return
adminUserId
;
}
public
void
setAdminUserId
(
String
adminUserId
)
{
this
.
adminUserId
=
adminUserId
;
}
}
computility-module-member/src/main/java/com/luhu/computility/module/member/config/MemberNewApiResponse.java
0 → 100644
View file @
691af29b
package
com
.
luhu
.
computility
.
module
.
member
.
config
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
/**
* New API 通用响应(会员模块专用)
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public
class
MemberNewApiResponse
<
T
>
{
private
Boolean
success
;
private
String
message
;
private
T
data
;
}
computility-module-member/src/main/java/com/luhu/computility/module/member/controller/admin/notify/MemberRechargeWpgjPayController.java
View file @
691af29b
...
...
@@ -70,6 +70,15 @@ public class MemberRechargeWpgjPayController {
if
(
WpgjOrderStatusEnum
.
isSuccess
(
orderStatus
))
{
// 支付成功:更新充值状态为已支付
memberRechargeService
.
updateRechargeStatus
(
Long
.
parseLong
(
merOrderId
),
1
);
// 3. 执行充值兑换(调用 new API 增加额度)
try
{
boolean
redeemSuccess
=
memberRechargeService
.
executeRecharge
(
Long
.
parseLong
(
merOrderId
));
log
.
info
(
"[notifyWpgjPay][Member] 充值兑换结果: {}, 商户订单号: {}"
,
redeemSuccess
,
merOrderId
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[notifyWpgjPay][Member] 充值兑换异常,商户订单号: {}"
,
merOrderId
,
e
);
}
log
.
info
(
"[notifyWpgjPay][Member] 支付成功处理完成,商户订单号: {}"
,
merOrderId
);
}
else
if
(
WpgjOrderStatusEnum
.
isFailedOrClosed
(
orderStatus
))
{
// 支付失败/关闭:更新充值状态为支付失败
...
...
computility-module-member/src/main/java/com/luhu/computility/module/member/controller/admin/recharge/vo/MemberRechargeRespVO.java
View file @
691af29b
...
...
@@ -3,6 +3,7 @@ package com.luhu.computility.module.member.controller.admin.recharge.vo;
import
io.swagger.v3.oas.annotations.media.Schema
;
import
lombok.Data
;
import
java.math.BigDecimal
;
import
java.time.LocalDateTime
;
@Schema
(
description
=
"管理后台 - 充值记录响应 VO"
)
...
...
@@ -57,4 +58,16 @@ public class MemberRechargeRespVO {
@Schema
(
description
=
"展示模式"
)
private
String
displayMode
;
@Schema
(
description
=
"兑换码"
)
private
String
redemptionCode
;
@Schema
(
description
=
"兑换额度"
)
private
Long
redemptionQuota
;
@Schema
(
description
=
"是否已兑换(0-未兑换,1-已兑换,-1-兑换失败)"
)
private
Integer
redeemed
;
@Schema
(
description
=
"兑换状态名称"
)
private
String
redeemedName
;
}
computility-module-member/src/main/java/com/luhu/computility/module/member/dal/dataobject/recharge/MemberRechargeDO.java
View file @
691af29b
...
...
@@ -71,4 +71,19 @@ public class MemberRechargeDO extends BaseDO {
*/
private
String
remark
;
/**
* 兑换码
*/
private
String
redemptionCode
;
/**
* 兑换额度(new api quota)
*/
private
Long
redemptionQuota
;
/**
* 是否已兑换(0-未兑换,1-已兑换,-1-兑换失败)
*/
private
Integer
redeemed
;
}
computility-module-member/src/main/java/com/luhu/computility/module/member/service/recharge/MemberRechargeService.java
View file @
691af29b
...
...
@@ -48,4 +48,10 @@ public interface MemberRechargeService {
*/
PageResult
<
MemberRechargeRespVO
>
getRechargePage
(
MemberRechargePageReqVO
reqVO
);
/**
* 执行充值兑换(支付成功后调用)
* @return 是否兑换成功
*/
boolean
executeRecharge
(
Long
rechargeId
);
}
computility-module-member/src/main/java/com/luhu/computility/module/member/service/recharge/MemberRechargeServiceImpl.java
View file @
691af29b
package
com
.
luhu
.
computility
.
module
.
member
.
service
.
recharge
;
import
com.luhu.computility.framework.common.pojo.PageResult
;
import
com.luhu.computility.module.member.config.MemberNewApiClient
;
import
com.luhu.computility.module.member.config.MemberNewApiProperties
;
import
com.luhu.computility.module.member.config.MemberNewApiResponse
;
import
com.luhu.computility.module.member.controller.admin.recharge.vo.MemberRechargePageReqVO
;
import
com.luhu.computility.module.member.controller.admin.recharge.vo.MemberRechargeRespVO
;
import
com.luhu.computility.module.member.dal.dataobject.recharge.MemberRechargeDO
;
import
com.luhu.computility.module.member.dal.dataobject.user.MemberUserDO
;
import
com.luhu.computility.module.member.dal.mysql.recharge.MemberRechargeMapper
;
import
com.luhu.computility.module.member.service.user.MemberUserService
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.stereotype.Service
;
...
...
@@ -21,6 +26,9 @@ import java.util.stream.Collectors;
public
class
MemberRechargeServiceImpl
implements
MemberRechargeService
{
private
final
MemberRechargeMapper
rechargeMapper
;
private
final
MemberUserService
memberUserService
;
private
final
MemberNewApiClient
memberNewApiClient
;
private
final
MemberNewApiProperties
newApiProperties
;
private
static
final
Map
<
String
,
String
>
PAY_CHANNEL_NAMES
=
Map
.
of
(
"wx"
,
"微信支付"
,
...
...
@@ -35,6 +43,12 @@ public class MemberRechargeServiceImpl implements MemberRechargeService {
2
,
"已退款"
);
private
static
final
Map
<
Integer
,
String
>
REDEEM_STATUS_NAMES
=
Map
.
of
(
-
1
,
"兑换失败"
,
0
,
"未兑换"
,
1
,
"已兑换"
);
/**
* 业务类型:3-会员充值(用于生成订单号)
*/
...
...
@@ -56,6 +70,7 @@ public class MemberRechargeServiceImpl implements MemberRechargeService {
.
payStatus
(
0
)
.
transactionId
(
transactionId
)
.
callbackStatus
(
0
)
.
redeemed
(
0
)
.
build
();
rechargeMapper
.
insert
(
recharge
);
log
.
info
(
"[创建充值记录] userId={}, amount={}, transactionId={}, orderId={}"
,
userId
,
amount
,
transactionId
,
orderId
);
...
...
@@ -104,6 +119,121 @@ public class MemberRechargeServiceImpl implements MemberRechargeService {
return
new
PageResult
<>(
voList
,
pageResult
.
getTotal
());
}
/**
* 执行充值兑换
* 流程:支付成功后调用
* 1. 用管理员权限创建兑换码
* 2. 用用户权限使用兑换码
*
* @param rechargeId 充值记录ID
* @return 是否兑换成功
*/
public
boolean
executeRecharge
(
Long
rechargeId
)
{
log
.
info
(
"[executeRecharge] 开始执行充值兑换,rechargeId={}"
,
rechargeId
);
// 1. 获取充值记录
MemberRechargeDO
recharge
=
rechargeMapper
.
selectById
(
rechargeId
);
if
(
recharge
==
null
)
{
log
.
error
(
"[executeRecharge] 充值记录不存在,rechargeId={}"
,
rechargeId
);
return
false
;
}
// 2. 检查是否已兑换
if
(
recharge
.
getRedeemed
()
!=
null
&&
recharge
.
getRedeemed
()
==
1
)
{
log
.
warn
(
"[executeRecharge] 该充值记录已兑换过,rechargeId={}"
,
rechargeId
);
return
true
;
}
// 3. 获取用户信息
MemberUserDO
user
=
memberUserService
.
getUser
(
recharge
.
getUserId
());
if
(
user
==
null
)
{
log
.
error
(
"[executeRecharge] 用户不存在,userId={}"
,
recharge
.
getUserId
());
updateRedeemedStatus
(
rechargeId
,
-
1
,
"用户不存在"
);
return
false
;
}
// 4. 检查用户是否有 new api 信息
if
(
user
.
getNewapiAccessToken
()
==
null
||
user
.
getNewapiUserId
()
==
null
)
{
log
.
error
(
"[executeRecharge] 用户未绑定new api账号,userId={}"
,
recharge
.
getUserId
());
updateRedeemedStatus
(
rechargeId
,
-
1
,
"用户未绑定new api账号"
);
return
false
;
}
// 5. 转换额度:金额 -> quota
long
quota
=
memberNewApiClient
.
convertToQuota
(
recharge
.
getAmount
());
log
.
info
(
"[executeRecharge] 转换额度:amount={} -> quota={}"
,
recharge
.
getAmount
(),
quota
);
// 6. 用管理员权限创建兑换码
String
adminToken
=
newApiProperties
.
getAdminToken
();
MemberNewApiResponse
<
String
>
createResp
=
memberNewApiClient
.
createRedemption
(
adminToken
,
quota
,
recharge
.
getAmount
(),
1
,
"充值-"
+
rechargeId
);
if
(
createResp
.
getSuccess
()
==
null
||
!
createResp
.
getSuccess
())
{
log
.
error
(
"[executeRecharge] 创建兑换码失败:{}"
,
createResp
.
getMessage
());
updateRedeemedStatus
(
rechargeId
,
-
1
,
"创建兑换码失败: "
+
createResp
.
getMessage
());
return
false
;
}
// 7. 解析兑换码
String
redemptionCode
=
createResp
.
getData
();
if
(
redemptionCode
==
null
||
redemptionCode
.
isEmpty
())
{
log
.
error
(
"[executeRecharge] 兑换码为空"
);
updateRedeemedStatus
(
rechargeId
,
-
1
,
"兑换码为空"
);
return
false
;
}
log
.
info
(
"[executeRecharge] 创建兑换码成功,code={}"
,
redemptionCode
);
// 8. 用用户权限使用兑换码
String
userAccessToken
=
user
.
getNewapiAccessToken
();
Integer
newapiUserId
=
Integer
.
parseInt
(
user
.
getNewapiUserId
().
toString
());
MemberNewApiResponse
<
Long
>
useResp
=
memberNewApiClient
.
useRedemption
(
userAccessToken
,
newapiUserId
,
redemptionCode
);
if
(
useResp
.
getSuccess
()
==
null
||
!
useResp
.
getSuccess
())
{
log
.
error
(
"[executeRecharge] 使用兑换码失败:{}"
,
useResp
.
getMessage
());
// 保存兑换码和失败状态
updateRedeemedWithCode
(
rechargeId
,
-
1
,
redemptionCode
,
quota
,
"使用兑换码失败: "
+
useResp
.
getMessage
());
return
false
;
}
log
.
info
(
"[executeRecharge] 使用兑换码成功,增加额度:{}"
,
useResp
.
getData
());
// 9. 更新兑换状态
updateRedeemedWithCode
(
rechargeId
,
1
,
redemptionCode
,
quota
,
null
);
log
.
info
(
"[executeRecharge] 充值兑换完成,rechargeId={}, quota={}"
,
rechargeId
,
quota
);
return
true
;
}
/**
* 更新兑换状态(不包含兑换码)
*/
private
void
updateRedeemedStatus
(
Long
id
,
Integer
redeemed
,
String
remark
)
{
MemberRechargeDO
updateDO
=
MemberRechargeDO
.
builder
().
build
();
updateDO
.
setId
(
id
);
updateDO
.
setRedeemed
(
redeemed
);
if
(
remark
!=
null
)
{
updateDO
.
setRemark
(
remark
);
}
rechargeMapper
.
updateById
(
updateDO
);
}
/**
* 更新兑换状态(包含兑换码)
*/
private
void
updateRedeemedWithCode
(
Long
id
,
Integer
redeemed
,
String
code
,
Long
quota
,
String
remark
)
{
MemberRechargeDO
updateDO
=
MemberRechargeDO
.
builder
().
build
();
updateDO
.
setId
(
id
);
updateDO
.
setRedeemed
(
redeemed
);
updateDO
.
setRedemptionCode
(
code
);
updateDO
.
setRedemptionQuota
(
quota
);
if
(
remark
!=
null
)
{
updateDO
.
setRemark
(
remark
);
}
rechargeMapper
.
updateById
(
updateDO
);
}
private
MemberRechargeRespVO
convertToRespVO
(
MemberRechargeDO
recharge
)
{
MemberRechargeRespVO
vo
=
new
MemberRechargeRespVO
();
vo
.
setId
(
recharge
.
getId
());
...
...
@@ -120,6 +250,12 @@ public class MemberRechargeServiceImpl implements MemberRechargeService {
vo
.
setRemark
(
recharge
.
getRemark
());
vo
.
setCreateTime
(
recharge
.
getCreateTime
());
vo
.
setUpdateTime
(
recharge
.
getUpdateTime
());
vo
.
setRedeemed
(
recharge
.
getRedeemed
());
if
(
recharge
.
getRedeemed
()
!=
null
)
{
vo
.
setRedeemedName
(
REDEEM_STATUS_NAMES
.
getOrDefault
(
recharge
.
getRedeemed
(),
"未知"
));
}
vo
.
setRedemptionCode
(
recharge
.
getRedemptionCode
());
vo
.
setRedemptionQuota
(
recharge
.
getRedemptionQuota
());
return
vo
;
}
...
...
computility-server/src/main/resources/application-local.yaml
View file @
691af29b
...
...
@@ -213,6 +213,8 @@ wx:
computility
:
new-api
:
base-url
:
http://172.25.164.0:3000
admin-token
:
zpdZ5iRZmEnaENEPvZbwaaDwMSUl1JE=
admin-user-id
:
1
captcha
:
enable
:
false
# 本地环境,暂时关闭图片验证码,方便登录等接口的测试;
security
:
...
...
@@ -245,7 +247,7 @@ computility:
-----END PRIVATE KEY-----
notify-url-compute
:
https://ric.admin.lijinqi.com/admin-api/compute/wpgj/notify
# 算力资源模块WPGJ回调地址
notify-url-api
:
https://ric.admin.lijinqi.com/admin-api/apihub/wpgj/notify
# APIHub模块WPGJ回调地址
notify-url-member
:
https://
ric.admin.lijinqi.com/admin-api/member/wpgj/notify
# 会员充值模块WPGJ回调地址
notify-url-member
:
https://
favoring-bobtail-jugular.ngrok-free.dev/admin-api/member/wpgj/notify
# 会员充值模块WPGJ回调地址
access-log
:
# 访问日志的配置项
enable
:
false
demo
:
false
# 关闭演示模式
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment