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
38869e0a
authored
Jun 05, 2026
by
renyizhao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
获取模型列表接口
parent
8cbaffa1
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
683 additions
and
0 deletions
+683
-0
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/app/aimodel/AppAiModelController.java
+430
-0
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/app/aimodel/vo/AppModelPricingVO.java
+59
-0
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/app/aimodel/vo/AppModelVO.java
+39
-0
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/app/aimodel/vo/AppPricingVO.java
+34
-0
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/service/newapi/NewApiClient.java
+121
-0
No files found.
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/app/aimodel/AppAiModelController.java
0 → 100644
View file @
38869e0a
package
com
.
luhu
.
computility
.
module
.
apihub
.
controller
.
app
.
aimodel
;
import
cn.hutool.core.util.StrUtil
;
import
com.luhu.computility.framework.common.pojo.CommonResult
;
import
com.luhu.computility.framework.security.core.util.SecurityFrameworkUtils
;
import
com.luhu.computility.module.apihub.controller.admin.newapi.NewApiResponse
;
import
com.luhu.computility.module.apihub.controller.app.aimodel.vo.AppModelPricingVO
;
import
com.luhu.computility.module.apihub.controller.app.aimodel.vo.AppModelVO
;
import
com.luhu.computility.module.apihub.controller.app.aimodel.vo.AppPricingVO
;
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
io.swagger.v3.oas.annotations.Operation
;
import
io.swagger.v3.oas.annotations.tags.Tag
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RequestParam
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.annotation.Resource
;
import
java.math.BigDecimal
;
import
java.math.RoundingMode
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
/**
* APP端 - AI 模型与定价接口
*/
@Slf4j
@RestController
@RequestMapping
(
"/app/ai"
)
@Tag
(
name
=
"APP端 - AI模型与定价"
)
public
class
AppAiModelController
{
/**
* 业务倍率系数:puhui 在 New API 价格基础上额外乘的固定系数
*/
private
static
final
BigDecimal
BUSINESS_RATIO
=
new
BigDecimal
(
"2"
);
@Resource
private
NewApiClient
newApiClient
;
@Resource
private
MemberUserService
memberUserService
;
/**
* 获取定价信息(原始)
*/
@GetMapping
(
"/pricing"
)
@Operation
(
summary
=
"获取 New API 定价信息(含模型定价和分组倍率,按当前登录用户权限)"
)
public
CommonResult
<
AppPricingVO
>
getPricing
()
{
String
accessToken
=
getCurrentUserAccessToken
();
String
userId
=
getCurrentUserNewApiId
();
NewApiResponse
<
Map
<
String
,
Object
>>
resp
=
newApiClient
.
getPricing
(
accessToken
,
userId
);
if
(
resp
.
getSuccess
()
==
null
||
!
resp
.
getSuccess
()
||
resp
.
getData
()
==
null
)
{
log
.
error
(
"[getPricing] 调用 New API 失败:{}"
,
resp
.
getMessage
());
return
CommonResult
.
error
(
1
,
"获取定价失败: "
+
resp
.
getMessage
());
}
return
CommonResult
.
success
(
buildPricingVO
(
resp
.
getData
()));
}
/**
* 获取模型列表(带元数据)
*/
@GetMapping
(
"/models"
)
@Operation
(
summary
=
"获取 New API 模型列表(带元数据和图标)"
)
public
CommonResult
<
List
<
AppModelVO
>>
getModels
(
@RequestParam
(
defaultValue
=
"0"
)
Integer
page
,
@RequestParam
(
defaultValue
=
"100"
)
Integer
pageSize
)
{
NewApiResponse
<
Map
<
String
,
Object
>>
resp
=
newApiClient
.
getModels
(
page
,
pageSize
);
if
(
resp
.
getSuccess
()
==
null
||
!
resp
.
getSuccess
()
||
resp
.
getData
()
==
null
)
{
log
.
error
(
"[getModels] 调用 New API 失败:{}"
,
resp
.
getMessage
());
return
CommonResult
.
error
(
1
,
"获取模型列表失败: "
+
resp
.
getMessage
());
}
@SuppressWarnings
(
"unchecked"
)
List
<
Map
<
String
,
Object
>>
items
=
(
List
<
Map
<
String
,
Object
>>)
resp
.
getData
().
get
(
"items"
);
if
(
items
==
null
)
{
return
CommonResult
.
success
(
Collections
.
emptyList
());
}
// 顺便获取 vendor 用于图标 fallback
List
<
Map
<
String
,
Object
>>
vendors
=
null
;
NewApiResponse
<
Map
<
String
,
Object
>>
pricingResp
=
newApiClient
.
getPricing
(
null
,
null
);
if
(
pricingResp
.
getSuccess
()
!=
null
&&
pricingResp
.
getSuccess
()
&&
pricingResp
.
getData
()
!=
null
)
{
@SuppressWarnings
(
"unchecked"
)
List
<
Map
<
String
,
Object
>>
v
=
(
List
<
Map
<
String
,
Object
>>)
pricingResp
.
getData
().
get
(
"vendors"
);
vendors
=
v
;
}
List
<
AppModelVO
>
result
=
new
ArrayList
<>();
for
(
Map
<
String
,
Object
>
item
:
items
)
{
AppModelVO
vo
=
new
AppModelVO
();
vo
.
setId
(
toLong
(
item
.
get
(
"id"
)));
vo
.
setModelName
((
String
)
item
.
get
(
"model_name"
));
vo
.
setIcon
((
String
)
item
.
get
(
"icon"
));
vo
.
setStatus
(
toInteger
(
item
.
get
(
"status"
)));
vo
.
setDescription
((
String
)
item
.
get
(
"description"
));
// 解析 enable_groups
Object
eg
=
item
.
get
(
"enable_groups"
);
if
(
eg
instanceof
List
)
{
@SuppressWarnings
(
"unchecked"
)
List
<
String
>
enableGroups
=
(
List
<
String
>)
eg
;
vo
.
setEnableGroups
(
enableGroups
);
}
// 解析 endpoints(JSON 字符串)
Object
ep
=
item
.
get
(
"endpoints"
);
if
(
ep
instanceof
String
)
{
try
{
vo
.
setEndpoints
(
com
.
luhu
.
computility
.
framework
.
common
.
util
.
json
.
JsonUtils
.
parseArray
((
String
)
ep
,
String
.
class
));
}
catch
(
Exception
e
)
{
log
.
warn
(
"[getModels] 解析 endpoints 失败:{}"
,
ep
);
}
}
else
if
(
ep
instanceof
List
)
{
@SuppressWarnings
(
"unchecked"
)
List
<
String
>
endpoints
=
(
List
<
String
>)
ep
;
vo
.
setEndpoints
(
endpoints
);
}
// vendor 名称:如果模型没配 icon,用 model_name 推断
if
(
StrUtil
.
isBlank
(
vo
.
getIcon
()))
{
vo
.
setIcon
(
resolveIconByName
(
vo
.
getModelName
(),
vendors
));
}
if
(
StrUtil
.
isBlank
(
vo
.
getVendorName
()))
{
vo
.
setVendorName
(
resolveVendorName
(
vo
.
getModelName
(),
vendors
));
}
result
.
add
(
vo
);
}
return
CommonResult
.
success
(
result
);
}
/**
* 聚合接口:模型列表 + 定价
*/
@GetMapping
(
"/models-with-pricing"
)
@Operation
(
summary
=
"获取模型列表及定价(已按当前用户分组倍率计算好价格)"
)
public
CommonResult
<
List
<
AppModelPricingVO
>>
getModelsWithPricing
()
{
// 1. 用当前用户 token 调 /api/pricing,拿到该用户实际生效的 group_ratio
String
accessToken
=
getCurrentUserAccessToken
();
String
userId
=
getCurrentUserNewApiId
();
NewApiResponse
<
Map
<
String
,
Object
>>
pricingResp
=
newApiClient
.
getPricing
(
accessToken
,
userId
);
if
(
pricingResp
.
getSuccess
()
==
null
||
!
pricingResp
.
getSuccess
()
||
pricingResp
.
getData
()
==
null
)
{
log
.
error
(
"[getModelsWithPricing] 获取定价失败:{}"
,
pricingResp
.
getMessage
());
return
CommonResult
.
error
(
1
,
"获取定价失败"
);
}
Map
<
String
,
Object
>
pricingData
=
pricingResp
.
getData
();
// 业务最终倍率 = group_ratio.default × 业务系数
BigDecimal
groupRatio
=
resolveGroupRatio
(
pricingData
);
// 2. 构造 model_name -> pricing 索引
@SuppressWarnings
(
"unchecked"
)
List
<
Map
<
String
,
Object
>>
modelPricingList
=
(
List
<
Map
<
String
,
Object
>>)
pricingData
.
get
(
"data"
);
Map
<
String
,
Map
<
String
,
Object
>>
pricingIndex
=
new
java
.
util
.
HashMap
<>();
if
(
modelPricingList
!=
null
)
{
for
(
Map
<
String
,
Object
>
p
:
modelPricingList
)
{
Object
name
=
p
.
get
(
"model_name"
);
if
(
name
!=
null
)
{
pricingIndex
.
put
(
name
.
toString
(),
p
);
}
}
}
// 3. 获取供应商
@SuppressWarnings
(
"unchecked"
)
List
<
Map
<
String
,
Object
>>
vendors
=
(
List
<
Map
<
String
,
Object
>>)
pricingData
.
get
(
"vendors"
);
// 4. 拉取所有模型元数据(分页拉满)
NewApiResponse
<
Map
<
String
,
Object
>>
modelsResp
=
newApiClient
.
getModels
(
0
,
200
);
if
(
modelsResp
.
getSuccess
()
==
null
||
!
modelsResp
.
getSuccess
()
||
modelsResp
.
getData
()
==
null
)
{
log
.
error
(
"[getModelsWithPricing] 获取模型列表失败:{}"
,
modelsResp
.
getMessage
());
return
CommonResult
.
error
(
1
,
"获取模型列表失败"
);
}
@SuppressWarnings
(
"unchecked"
)
List
<
Map
<
String
,
Object
>>
items
=
(
List
<
Map
<
String
,
Object
>>)
modelsResp
.
getData
().
get
(
"items"
);
if
(
items
==
null
)
{
return
CommonResult
.
success
(
Collections
.
emptyList
());
}
List
<
AppModelPricingVO
>
result
=
new
ArrayList
<>();
for
(
Map
<
String
,
Object
>
item
:
items
)
{
String
modelName
=
(
String
)
item
.
get
(
"model_name"
);
if
(
modelName
==
null
)
continue
;
AppModelPricingVO
vo
=
new
AppModelPricingVO
();
vo
.
setModelName
(
modelName
);
vo
.
setIcon
((
String
)
item
.
get
(
"icon"
));
vo
.
setStatus
(
toInteger
(
item
.
get
(
"status"
)));
vo
.
setDescription
((
String
)
item
.
get
(
"description"
));
// endpoints
Object
ep
=
item
.
get
(
"endpoints"
);
if
(
ep
instanceof
String
)
{
try
{
vo
.
setSupportedEndpoints
(
com
.
luhu
.
computility
.
framework
.
common
.
util
.
json
.
JsonUtils
.
parseArray
((
String
)
ep
,
String
.
class
));
}
catch
(
Exception
ignored
)
{
}
}
else
if
(
ep
instanceof
List
)
{
@SuppressWarnings
(
"unchecked"
)
List
<
String
>
eps
=
(
List
<
String
>)
ep
;
vo
.
setSupportedEndpoints
(
eps
);
}
// enable_groups
Object
eg
=
item
.
get
(
"enable_groups"
);
if
(
eg
instanceof
List
)
{
@SuppressWarnings
(
"unchecked"
)
List
<
String
>
egs
=
(
List
<
String
>)
eg
;
vo
.
setEnableGroups
(
egs
);
}
// 合并 pricing
Map
<
String
,
Object
>
pricing
=
pricingIndex
.
get
(
modelName
);
if
(
pricing
!=
null
)
{
vo
.
setQuotaType
(
toInteger
(
pricing
.
get
(
"quota_type"
)));
vo
.
setModelRatio
(
toBigDecimal
(
pricing
.
get
(
"model_ratio"
)));
vo
.
setModelPrice
(
toBigDecimal
(
pricing
.
get
(
"model_price"
)));
vo
.
setCompletionRatio
(
toBigDecimal
(
pricing
.
get
(
"completion_ratio"
)));
// 计算价格
BigDecimal
modelPrice
=
vo
.
getModelPrice
()
!=
null
?
vo
.
getModelPrice
()
:
BigDecimal
.
ZERO
;
BigDecimal
modelRatio
=
vo
.
getModelRatio
()
!=
null
?
vo
.
getModelRatio
()
:
BigDecimal
.
ZERO
;
BigDecimal
completionRatio
=
vo
.
getCompletionRatio
()
!=
null
?
vo
.
getCompletionRatio
()
:
BigDecimal
.
ONE
;
if
(
vo
.
getQuotaType
()
!=
null
&&
vo
.
getQuotaType
()
==
1
)
{
// 固定价模式:业务系数不参与
vo
.
setInputPrice
(
modelPrice
.
setScale
(
2
,
RoundingMode
.
HALF_UP
));
vo
.
setOutputPrice
(
modelPrice
.
multiply
(
completionRatio
).
setScale
(
2
,
RoundingMode
.
HALF_UP
));
}
else
{
// 倍率模式
vo
.
setInputPrice
(
modelPrice
.
add
(
modelRatio
).
multiply
(
groupRatio
).
setScale
(
2
,
RoundingMode
.
HALF_UP
));
vo
.
setOutputPrice
(
modelPrice
.
add
(
modelRatio
.
multiply
(
completionRatio
)).
multiply
(
groupRatio
).
setScale
(
2
,
RoundingMode
.
HALF_UP
));
}
}
vo
.
setGroupRatio
(
groupRatio
);
// icon & vendor 推断
if
(
StrUtil
.
isBlank
(
vo
.
getIcon
()))
{
vo
.
setIcon
(
resolveIconByName
(
modelName
,
vendors
));
}
vo
.
setVendorName
(
resolveVendorName
(
modelName
,
vendors
));
result
.
add
(
vo
);
}
return
CommonResult
.
success
(
result
);
}
/**
* 从 pricing 原始数据中解析业务最终倍率 = group_ratio.default × 业务系数
*/
private
BigDecimal
resolveGroupRatio
(
Map
<
String
,
Object
>
pricingData
)
{
@SuppressWarnings
(
"unchecked"
)
Map
<
String
,
Object
>
groupRatioMap
=
(
Map
<
String
,
Object
>)
pricingData
.
get
(
"group_ratio"
);
if
(
groupRatioMap
==
null
)
{
return
BUSINESS_RATIO
;
}
Object
defaultRatio
=
groupRatioMap
.
get
(
"default"
);
if
(
defaultRatio
==
null
)
{
return
BUSINESS_RATIO
;
}
try
{
BigDecimal
base
=
new
BigDecimal
(
String
.
valueOf
(
defaultRatio
));
return
base
.
multiply
(
BUSINESS_RATIO
);
}
catch
(
Exception
e
)
{
log
.
warn
(
"[resolveGroupRatio] 解析 group_ratio.default 失败:{}"
,
defaultRatio
);
return
BUSINESS_RATIO
;
}
}
/**
* 获取当前登录用户的 New API accessToken(无则返回 null)
*/
private
String
getCurrentUserAccessToken
()
{
MemberUserDO
user
=
getCurrentMemberUser
();
return
user
==
null
?
null
:
user
.
getNewapiAccessToken
();
}
/**
* 获取当前登录用户在 New API 的 userId(无则返回 null)
*/
private
String
getCurrentUserNewApiId
()
{
MemberUserDO
user
=
getCurrentMemberUser
();
return
user
==
null
||
user
.
getNewapiUserId
()
==
null
?
null
:
user
.
getNewapiUserId
().
toString
();
}
/**
* 获取当前登录用户的 MemberUserDO
*/
private
MemberUserDO
getCurrentMemberUser
()
{
try
{
Long
userId
=
SecurityFrameworkUtils
.
getLoginUserId
();
if
(
userId
==
null
)
{
return
null
;
}
return
memberUserService
.
getUser
(
userId
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[getCurrentMemberUser] 异常"
,
e
);
return
null
;
}
}
/**
* 构造定价 VO
*/
private
AppPricingVO
buildPricingVO
(
Map
<
String
,
Object
>
data
)
{
AppPricingVO
vo
=
new
AppPricingVO
();
// 分组倍率
@SuppressWarnings
(
"unchecked"
)
Map
<
String
,
Object
>
groupRatioMap
=
(
Map
<
String
,
Object
>)
data
.
get
(
"group_ratio"
);
if
(
groupRatioMap
!=
null
)
{
Map
<
String
,
BigDecimal
>
ratios
=
new
java
.
util
.
HashMap
<>();
groupRatioMap
.
forEach
((
k
,
v
)
->
{
try
{
ratios
.
put
(
k
,
new
BigDecimal
(
String
.
valueOf
(
v
)));
}
catch
(
Exception
ignored
)
{
}
});
vo
.
setGroupRatios
(
ratios
);
}
// 当前用户实际生效倍率(已含业务系数)
vo
.
setGroupRatio
(
resolveGroupRatio
(
data
));
// 支持的端点
@SuppressWarnings
(
"unchecked"
)
Map
<
String
,
Object
>
supportedEndpoint
=
(
Map
<
String
,
Object
>)
data
.
get
(
"supported_endpoint"
);
vo
.
setSupportedEndpoint
(
supportedEndpoint
);
// 供应商列表
@SuppressWarnings
(
"unchecked"
)
List
<
Map
<
String
,
Object
>>
vendors
=
(
List
<
Map
<
String
,
Object
>>)
data
.
get
(
"vendors"
);
vo
.
setVendors
(
vendors
);
// 模型定价(原始)
@SuppressWarnings
(
"unchecked"
)
List
<
Map
<
String
,
Object
>>
modelPricing
=
(
List
<
Map
<
String
,
Object
>>)
data
.
get
(
"data"
);
vo
.
setModelPricing
(
modelPricing
);
return
vo
;
}
/**
* 根据 model_name 推断 icon 名
*/
private
String
resolveIconByName
(
String
modelName
,
List
<
Map
<
String
,
Object
>>
vendors
)
{
if
(
modelName
==
null
)
return
"AI"
;
String
lower
=
modelName
.
toLowerCase
();
if
(
lower
.
contains
(
"qwen"
))
return
"Qwen.Color"
;
if
(
lower
.
contains
(
"deepseek"
))
return
"DeepSeek.Color"
;
if
(
lower
.
contains
(
"doubao"
))
return
"Doubao.Color"
;
if
(
lower
.
contains
(
"gpt"
)
||
lower
.
contains
(
"openai"
)
||
lower
.
contains
(
"o1"
)
||
lower
.
contains
(
"o3"
)
||
lower
.
contains
(
"o4"
))
return
"OpenAI"
;
if
(
lower
.
contains
(
"claude"
))
return
"Claude.Color"
;
if
(
lower
.
contains
(
"gemini"
))
return
"Gemini.Color"
;
if
(
lower
.
contains
(
"llama"
)
||
lower
.
contains
(
"meta"
))
return
"Meta"
;
if
(
lower
.
contains
(
"moonshot"
)
||
lower
.
contains
(
"kimi"
))
return
"Moonshot"
;
if
(
lower
.
contains
(
"glm"
)
||
lower
.
contains
(
"zhipu"
)
||
lower
.
contains
(
"chatglm"
))
return
"Zhipu.Color"
;
if
(
lower
.
contains
(
"embedding"
)
||
lower
.
contains
(
"bge"
)
||
lower
.
contains
(
"m3e"
)
||
lower
.
contains
(
"bge-m3"
))
return
"Embedding"
;
if
(
lower
.
contains
(
"rerank"
))
return
"Rerank"
;
if
(
lower
.
contains
(
"stable"
)
||
lower
.
contains
(
"sdxl"
))
return
"StableDiffusion"
;
if
(
lower
.
contains
(
"wav2lip"
)
||
lower
.
contains
(
"tts"
)
||
lower
.
contains
(
"asr"
)
||
lower
.
contains
(
"audio"
))
return
"Audio"
;
if
(
lower
.
contains
(
"mistral"
)
||
lower
.
contains
(
"mixtral"
))
return
"Mistral"
;
if
(
lower
.
contains
(
"yi"
))
return
"Yi"
;
if
(
lower
.
contains
(
"baichuan"
))
return
"Baichuan"
;
if
(
lower
.
contains
(
"minimax"
))
return
"MiniMax.Color"
;
if
(
lower
.
contains
(
"hunyuan"
)
||
lower
.
contains
(
"tencent"
))
return
"Hunyuan"
;
if
(
lower
.
contains
(
"spark"
)
||
lower
.
contains
(
"xunfei"
)
||
lower
.
contains
(
"iflytek"
))
return
"Spark"
;
if
(
lower
.
contains
(
"sensenova"
)
||
lower
.
contains
(
"sense"
))
return
"SenseNova"
;
return
"AI"
;
}
/**
* 根据 model_name 推断 vendor 名
*/
private
String
resolveVendorName
(
String
modelName
,
List
<
Map
<
String
,
Object
>>
vendors
)
{
if
(
modelName
==
null
)
return
null
;
if
(
vendors
!=
null
)
{
for
(
Map
<
String
,
Object
>
v
:
vendors
)
{
Object
name
=
v
.
get
(
"name"
);
if
(
name
==
null
)
continue
;
String
n
=
name
.
toString
().
toLowerCase
();
if
(
modelName
.
toLowerCase
().
contains
(
n
))
{
return
name
.
toString
();
}
}
}
return
null
;
}
private
Long
toLong
(
Object
o
)
{
if
(
o
==
null
)
return
null
;
if
(
o
instanceof
Number
)
return
((
Number
)
o
).
longValue
();
try
{
return
Long
.
parseLong
(
o
.
toString
());
}
catch
(
Exception
ignored
)
{
return
null
;
}
}
private
Integer
toInteger
(
Object
o
)
{
if
(
o
==
null
)
return
null
;
if
(
o
instanceof
Number
)
return
((
Number
)
o
).
intValue
();
try
{
return
Integer
.
parseInt
(
o
.
toString
());
}
catch
(
Exception
ignored
)
{
return
null
;
}
}
private
BigDecimal
toBigDecimal
(
Object
o
)
{
if
(
o
==
null
)
return
null
;
if
(
o
instanceof
Number
)
return
new
BigDecimal
(
o
.
toString
());
if
(
o
instanceof
String
)
{
try
{
return
new
BigDecimal
((
String
)
o
);
}
catch
(
Exception
ignored
)
{
return
null
;
}
}
return
null
;
}
}
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/app/aimodel/vo/AppModelPricingVO.java
0 → 100644
View file @
38869e0a
package
com
.
luhu
.
computility
.
module
.
apihub
.
controller
.
app
.
aimodel
.
vo
;
import
io.swagger.v3.oas.annotations.media.Schema
;
import
lombok.Data
;
import
java.math.BigDecimal
;
import
java.util.List
;
import
java.util.Map
;
/**
* APP端 - 模型定价信息 VO(聚合后)
*/
@Data
@Schema
(
description
=
"APP端 - 模型定价信息"
)
public
class
AppModelPricingVO
{
@Schema
(
description
=
"模型名"
)
private
String
modelName
;
@Schema
(
description
=
"图标名(lobehub 风格,如 Qwen.Color)"
)
private
String
icon
;
@Schema
(
description
=
"供应商名"
)
private
String
vendorName
;
@Schema
(
description
=
"支持的端点列表"
)
private
List
<
String
>
supportedEndpoints
;
@Schema
(
description
=
"支持的分组列表"
)
private
List
<
String
>
enableGroups
;
@Schema
(
description
=
"计价方式:0-按倍率,1-按固定价"
)
private
Integer
quotaType
;
@Schema
(
description
=
"模型倍率"
)
private
BigDecimal
modelRatio
;
@Schema
(
description
=
"固定价(元/1k tokens)"
)
private
BigDecimal
modelPrice
;
@Schema
(
description
=
"补全价格倍率"
)
private
BigDecimal
completionRatio
;
@Schema
(
description
=
"当前用户分组倍率"
)
private
BigDecimal
groupRatio
;
@Schema
(
description
=
"输入价格(元/1k tokens),已乘 groupRatio"
)
private
BigDecimal
inputPrice
;
@Schema
(
description
=
"输出价格(元/1k tokens),已乘 groupRatio"
)
private
BigDecimal
outputPrice
;
@Schema
(
description
=
"状态:1-启用,0-禁用"
)
private
Integer
status
;
@Schema
(
description
=
"模型描述(未设置则为空)"
)
private
String
description
;
}
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/app/aimodel/vo/AppModelVO.java
0 → 100644
View file @
38869e0a
package
com
.
luhu
.
computility
.
module
.
apihub
.
controller
.
app
.
aimodel
.
vo
;
import
io.swagger.v3.oas.annotations.media.Schema
;
import
lombok.Data
;
import
java.util.List
;
/**
* APP端 - 模型信息 VO
*/
@Data
@Schema
(
description
=
"APP端 - 模型信息"
)
public
class
AppModelVO
{
@Schema
(
description
=
"模型ID"
)
private
Long
id
;
@Schema
(
description
=
"模型名"
)
private
String
modelName
;
@Schema
(
description
=
"图标名"
)
private
String
icon
;
@Schema
(
description
=
"供应商名(自动匹配)"
)
private
String
vendorName
;
@Schema
(
description
=
"支持的端点"
)
private
List
<
String
>
endpoints
;
@Schema
(
description
=
"支持的分组"
)
private
List
<
String
>
enableGroups
;
@Schema
(
description
=
"状态:1-启用,0-禁用"
)
private
Integer
status
;
@Schema
(
description
=
"模型描述(未设置则为空)"
)
private
String
description
;
}
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/controller/app/aimodel/vo/AppPricingVO.java
0 → 100644
View file @
38869e0a
package
com
.
luhu
.
computility
.
module
.
apihub
.
controller
.
app
.
aimodel
.
vo
;
import
io.swagger.v3.oas.annotations.media.Schema
;
import
lombok.Data
;
import
java.util.List
;
import
java.util.Map
;
/**
* APP端 - 定价信息 VO
*/
@Data
@Schema
(
description
=
"APP端 - 定价信息"
)
public
class
AppPricingVO
{
@Schema
(
description
=
"当前用户所属分组名"
)
private
String
group
;
@Schema
(
description
=
"当前分组倍率"
)
private
java
.
math
.
BigDecimal
groupRatio
;
@Schema
(
description
=
"所有可用分组及倍率"
)
private
Map
<
String
,
java
.
math
.
BigDecimal
>
groupRatios
;
@Schema
(
description
=
"支持的端点"
)
private
Map
<
String
,
Object
>
supportedEndpoint
;
@Schema
(
description
=
"供应商列表(用于图标 fallback)"
)
private
List
<
Map
<
String
,
Object
>>
vendors
;
@Schema
(
description
=
"模型定价列表(原始数据)"
)
private
List
<
Map
<
String
,
Object
>>
modelPricing
;
}
computility-module-apihub/computility-module-apihub-biz/src/main/java/com/luhu/computility/module/apihub/service/newapi/NewApiClient.java
View file @
38869e0a
...
...
@@ -5,6 +5,8 @@ import cn.hutool.http.HttpRequest;
import
cn.hutool.http.HttpResponse
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
com.fasterxml.jackson.core.type.TypeReference
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.luhu.computility.module.apihub.config.NewApiProperties
;
import
com.luhu.computility.module.apihub.controller.admin.newapi.*
;
import
lombok.extern.slf4j.Slf4j
;
...
...
@@ -21,6 +23,9 @@ public class NewApiClient {
@Resource
private
NewApiProperties
newApiProperties
;
@Resource
private
ObjectMapper
objectMapper
;
private
static
final
String
HEADER_USER
=
"New-Api-User"
;
private
static
final
String
HEADER_AUTH
=
"Authorization"
;
private
static
final
String
USER_ANONYMOUS
=
"-1"
;
...
...
@@ -283,6 +288,122 @@ public class NewApiClient {
}
}
/**
* 获取定价信息(含模型定价、分组倍率、支持端点)
* GET /api/pricing
*
* @param accessToken 用户访问令牌(可空)。传入后使用对应账号权限请求,group_ratio.default 即为该用户实际倍率;不传则匿名请求。
* @param userId 用户在 New API 的 userId(与 accessToken 配套传入,用于 New-Api-User 头)。可空。
*/
public
NewApiResponse
<
Map
<
String
,
Object
>>
getPricing
(
String
accessToken
,
String
userId
)
{
String
url
=
newApiProperties
.
getBaseUrl
()
+
"/api/pricing"
;
log
.
info
(
"[NewApiClient.getPricing] 请求URL: {}, 带token={}, 带userId={}"
,
url
,
StrUtil
.
isNotBlank
(
accessToken
),
StrUtil
.
isNotBlank
(
userId
));
try
{
HttpRequest
request
=
HttpRequest
.
get
(
url
).
timeout
(
30000
);
if
(
StrUtil
.
isBlank
(
accessToken
))
{
// 匿名请求
request
.
header
(
HEADER_USER
,
USER_ANONYMOUS
);
}
else
{
// 带 token 时同时传 New-Api-User 头
if
(
StrUtil
.
isNotBlank
(
userId
))
{
request
.
header
(
HEADER_USER
,
userId
);
}
request
.
header
(
HEADER_AUTH
,
"Bearer "
+
accessToken
);
}
HttpResponse
response
=
request
.
execute
();
log
.
info
(
"[NewApiClient.getPricing] 响应状态: {}"
,
response
.
getStatus
());
return
parseMapResponse
(
response
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[NewApiClient.getPricing] 请求异常"
,
e
);
return
NewApiResponse
.<
Map
<
String
,
Object
>>
builder
()
.
success
(
false
)
.
message
(
"请求异常: "
+
e
.
getMessage
())
.
httpResponse
(
null
)
.
build
();
}
}
/**
* 获取模型列表(管理员权限)
* GET /api/models/?p=0&page_size=100
*/
public
NewApiResponse
<
Map
<
String
,
Object
>>
getModels
(
Integer
page
,
Integer
pageSize
)
{
String
url
=
newApiProperties
.
getBaseUrl
()
+
"/api/models/"
;
if
(
page
!=
null
||
pageSize
!=
null
)
{
StringBuilder
sb
=
new
StringBuilder
(
url
).
append
(
"?"
);
if
(
page
!=
null
)
sb
.
append
(
"p="
).
append
(
page
);
if
(
page
!=
null
&&
pageSize
!=
null
)
sb
.
append
(
"&"
);
if
(
pageSize
!=
null
)
sb
.
append
(
"page_size="
).
append
(
pageSize
);
url
=
sb
.
toString
();
}
log
.
info
(
"[NewApiClient.getModels] 请求URL: {}"
,
url
);
try
{
HttpResponse
response
=
HttpRequest
.
get
(
url
)
.
header
(
HEADER_USER
,
newApiProperties
.
getAdminUserId
())
.
header
(
HEADER_AUTH
,
"Bearer "
+
newApiProperties
.
getAdminToken
())
.
timeout
(
30000
)
.
execute
();
log
.
info
(
"[NewApiClient.getModels] 响应状态: {}"
,
response
.
getStatus
());
return
parseMapResponse
(
response
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[NewApiClient.getModels] 请求异常"
,
e
);
return
NewApiResponse
.<
Map
<
String
,
Object
>>
builder
()
.
success
(
false
)
.
message
(
"请求异常: "
+
e
.
getMessage
())
.
httpResponse
(
null
)
.
build
();
}
}
@SuppressWarnings
(
"unchecked"
)
private
NewApiResponse
<
Map
<
String
,
Object
>>
parseMapResponse
(
HttpResponse
response
)
{
try
{
String
body
=
response
.
body
();
if
(
StrUtil
.
isBlank
(
body
))
{
log
.
warn
(
"[NewApiClient.parseMapResponse] 响应为空,status={}"
,
response
.
getStatus
());
return
NewApiResponse
.<
Map
<
String
,
Object
>>
builder
()
.
success
(
false
)
.
message
(
"Empty response"
)
.
httpResponse
(
response
)
.
build
();
}
// 调试日志:记录原始响应体(截断避免过长)
String
bodyPreview
=
body
.
length
()
>
500
?
body
.
substring
(
0
,
500
)
+
"..."
:
body
;
log
.
info
(
"[NewApiClient.parseMapResponse] status={}, body={}"
,
response
.
getStatus
(),
bodyPreview
);
// 使用 Jackson 解析(兼容 New API 返回的含特殊字符 JSON)
Map
<
String
,
Object
>
root
=
objectMapper
.
readValue
(
body
,
new
TypeReference
<
Map
<
String
,
Object
>>()
{});
NewApiResponse
<
Map
<
String
,
Object
>>
result
=
new
NewApiResponse
<>();
result
.
setSuccess
(
root
.
get
(
"success"
)
==
null
?
null
:
Boolean
.
valueOf
(
root
.
get
(
"success"
).
toString
()));
result
.
setMessage
(
root
.
get
(
"message"
)
==
null
?
null
:
root
.
get
(
"message"
).
toString
());
result
.
setHttpResponse
(
response
);
if
(
root
.
containsKey
(
"data"
)
&&
root
.
get
(
"data"
)
!=
null
)
{
Object
data
=
root
.
get
(
"data"
);
if
(
data
instanceof
Map
)
{
result
.
setData
((
Map
<
String
,
Object
>)
data
);
}
else
{
// data 是其他类型(List 等),原样返回,调用方自行处理
Map
<
String
,
Object
>
wrapper
=
new
HashMap
<>();
wrapper
.
put
(
"data"
,
data
);
result
.
setData
(
wrapper
);
}
}
return
result
;
}
catch
(
Exception
e
)
{
log
.
error
(
"[NewApiClient.parseMapResponse] 解析响应异常,body={}"
,
response
.
body
(),
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
{
...
...
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