This commit is contained in:
2026-01-13 18:30:10 +08:00
parent 0206710e29
commit 04df8460a4
29 changed files with 2472 additions and 111 deletions

View File

@@ -67,6 +67,23 @@ type (
TodayProfitRate float64 `json:"today_profit_rate"` // 今日利润率
MonthProfitRate float64 `json:"month_profit_rate"` // 当月利润率
TotalProfitRate float64 `json:"total_profit_rate"` // 总利润率
// 今日明细
TodayDetail AdminProfitDetail `json:"today_detail"`
// 当月明细
MonthDetail AdminProfitDetail `json:"month_detail"`
// 总计明细
TotalDetail AdminProfitDetail `json:"total_detail"`
}
// 利润明细
AdminProfitDetail {
Revenue float64 `json:"revenue"` // 营收
Commission float64 `json:"commission"` // 佣金
Rebate float64 `json:"rebate"` // 返利
CompanyTax float64 `json:"company_tax"` // 税务成本
ApiCost float64 `json:"api_cost"` // API调用成本
TaxIncome float64 `json:"tax_income"` // 提现收税
Profit float64 `json:"profit"` // 利润
ProfitRate float64 `json:"profit_rate"` // 利润率
}
// 趋势数据
AdminTrendData {

View File

@@ -279,6 +279,10 @@ service main {
// 下架单个模块(创建订单并支付)
@handler OfflineFeature
post /whitelist/offline (OfflineFeatureReq) returns (OfflineFeatureResp)
// 检查订单是否属于当前代理推广
@handler CheckOrderAgent
get /order/agent (CheckOrderAgentReq) returns (CheckOrderAgentResp)
}
type (
@@ -705,6 +709,14 @@ type (
NeedPay bool `json:"need_pay"` // 是否需要发起支付
Amount float64 `json:"amount"` // 需要支付的金额单位0表示无需支付
}
// 检查订单是否属于当前代理请求
CheckOrderAgentReq {
OrderId string `form:"order_id"` // 订单ID
}
// 检查订单是否属于当前代理响应
CheckOrderAgentResp {
IsAgentOrder bool `json:"is_agent_order"` // 是否是当前代理推广的订单
}
)
// ============================================

View File

@@ -0,0 +1,29 @@
package agent
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"ycc-server/app/main/api/internal/logic/agent"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/result"
"ycc-server/pkg/lzkit/validator"
)
func CheckOrderAgentHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CheckOrderAgentReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := agent.NewCheckOrderAgentLogic(r.Context(), svcCtx)
resp, err := l.CheckOrderAgent(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -715,6 +715,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/level/privilege",
Handler: agent.GetLevelPrivilegeHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/order/agent",
Handler: agent.CheckOrderAgentHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/product_config",

View File

@@ -8,6 +8,7 @@ import (
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/service"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
@@ -224,7 +225,7 @@ func (l *AdminGetDashboardStatisticsLogic) calculateAgentStatistics(todayStart,
func (l *AdminGetDashboardStatisticsLogic) calculateProfitStatistics(todayStart, todayEnd, monthStart, monthEnd time.Time, revenueStats types.AdminRevenueStatistics) (types.AdminProfitStatistics, error) {
var stats types.AdminProfitStatistics
// 公司交税比例6%
// 税务成本比例6%
const companyTaxRate = 0.06
// 今日利润计算
@@ -244,20 +245,44 @@ func (l *AdminGetDashboardStatisticsLogic) calculateProfitStatistics(todayStart,
if err != nil {
return stats, err
}
// 今日公司交订单金额的6%
// 今日税务成本订单金额的6%
todayCompanyTax := todayRevenue * companyTaxRate
// 今日平台收入agent_withdrawal_tax表中tax_status=2的tax_amount总和
// 今日提现收agent_withdrawal_tax表中tax_status=2的tax_amount总和
todayTaxIncomeBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("del_state = ? AND tax_status = ? AND create_time >= ? AND create_time < ?", globalkey.DelStateNo, 2, todayStart, todayEnd)
todayTaxIncome, err := l.svcCtx.AgentWithdrawalTaxModel.FindSum(l.ctx, todayTaxIncomeBuilder, "tax_amount")
if err != nil {
return stats, err
}
// 今日利润 = 营收 - 佣金 - 返利 - 公司交税 + 平台收入税
stats.TodayProfit = todayRevenue - todayCommission - todayRebate - todayCompanyTax + todayTaxIncome
// 今日API调用成本
todayApiCost := 0.0
if l.svcCtx.TianyuanapiCallLogService != nil {
todayApiStats, err := l.svcCtx.TianyuanapiCallLogService.GetStatistics(l.ctx, service.StatisticsFilter{
StartDate: todayStart,
EndDate: todayEnd,
})
if err != nil {
logx.Errorf("获取今日API调用成本失败: %v", err)
} else {
todayApiCost = todayApiStats.TotalCost
}
}
// 今日利润 = 营收 - 佣金 - 返利 - 税务成本 - API调用成本 + 提现收税
stats.TodayProfit = todayRevenue - todayCommission - todayRebate - todayCompanyTax - todayApiCost + todayTaxIncome
if todayRevenue > 0 {
stats.TodayProfitRate = stats.TodayProfit / todayRevenue * 100
}
// 今日明细
stats.TodayDetail = types.AdminProfitDetail{
Revenue: todayRevenue,
Commission: todayCommission,
Rebate: todayRebate,
CompanyTax: todayCompanyTax,
ApiCost: todayApiCost,
TaxIncome: todayTaxIncome,
Profit: stats.TodayProfit,
ProfitRate: stats.TodayProfitRate,
}
// 当月利润计算
// 当月营收
@@ -276,20 +301,44 @@ func (l *AdminGetDashboardStatisticsLogic) calculateProfitStatistics(todayStart,
if err != nil {
return stats, err
}
// 当月公司交
// 当月税务成本
monthCompanyTax := monthRevenue * companyTaxRate
// 当月平台收入
// 当月提现收
monthTaxIncomeBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("del_state = ? AND tax_status = ? AND create_time >= ? AND create_time < ?", globalkey.DelStateNo, 2, monthStart, monthEnd)
monthTaxIncome, err := l.svcCtx.AgentWithdrawalTaxModel.FindSum(l.ctx, monthTaxIncomeBuilder, "tax_amount")
if err != nil {
return stats, err
}
// 当月API调用成本
monthApiCost := 0.0
if l.svcCtx.TianyuanapiCallLogService != nil {
monthApiStats, err := l.svcCtx.TianyuanapiCallLogService.GetStatistics(l.ctx, service.StatisticsFilter{
StartDate: monthStart,
EndDate: monthEnd,
})
if err != nil {
logx.Errorf("获取当月API调用成本失败: %v", err)
} else {
monthApiCost = monthApiStats.TotalCost
}
}
// 当月利润
stats.MonthProfit = monthRevenue - monthCommission - monthRebate - monthCompanyTax + monthTaxIncome
stats.MonthProfit = monthRevenue - monthCommission - monthRebate - monthCompanyTax - monthApiCost + monthTaxIncome
if monthRevenue > 0 {
stats.MonthProfitRate = stats.MonthProfit / monthRevenue * 100
}
// 当月明细
stats.MonthDetail = types.AdminProfitDetail{
Revenue: monthRevenue,
Commission: monthCommission,
Rebate: monthRebate,
CompanyTax: monthCompanyTax,
ApiCost: monthApiCost,
TaxIncome: monthTaxIncome,
Profit: stats.MonthProfit,
ProfitRate: stats.MonthProfitRate,
}
// 总利润计算
// 总营收
@@ -303,25 +352,46 @@ func (l *AdminGetDashboardStatisticsLogic) calculateProfitStatistics(todayStart,
}
// 总返利
totalRebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("del_state = ? AND status != ?", globalkey.DelStateNo, 3)
Where("status != ?", 3)
totalRebate, err := l.svcCtx.AgentRebateModel.FindSum(l.ctx, totalRebateBuilder, "rebate_amount")
if err != nil {
return stats, err
}
// 总公司交
// 总税务成本
totalCompanyTax := totalRevenue * companyTaxRate
// 总平台收入
// 总提现收
totalTaxIncomeBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("del_state = ? AND tax_status = ?", globalkey.DelStateNo, 2)
Where("tax_status = ?", 2)
totalTaxIncome, err := l.svcCtx.AgentWithdrawalTaxModel.FindSum(l.ctx, totalTaxIncomeBuilder, "tax_amount")
if err != nil {
return stats, err
}
// 总API调用成本
totalApiCost := 0.0
if l.svcCtx.TianyuanapiCallLogService != nil {
totalApiStats, err := l.svcCtx.TianyuanapiCallLogService.GetStatistics(l.ctx, service.StatisticsFilter{})
if err != nil {
logx.Errorf("获取总API调用成本失败: %v", err)
} else {
totalApiCost = totalApiStats.TotalCost
}
}
// 总利润
stats.TotalProfit = totalRevenue - totalCommission - totalRebate - totalCompanyTax + totalTaxIncome
stats.TotalProfit = totalRevenue - totalCommission - totalRebate - totalCompanyTax - totalApiCost + totalTaxIncome
if totalRevenue > 0 {
stats.TotalProfitRate = stats.TotalProfit / totalRevenue * 100
}
// 总计明细
stats.TotalDetail = types.AdminProfitDetail{
Revenue: totalRevenue,
Commission: totalCommission,
Rebate: totalRebate,
CompanyTax: totalCompanyTax,
ApiCost: totalApiCost,
TaxIncome: totalTaxIncome,
Profit: stats.TotalProfit,
ProfitRate: stats.TotalProfitRate,
}
return stats, nil
}

View File

@@ -0,0 +1,46 @@
package agent
import (
"context"
"ycc-server/app/main/api/internal/logic/query"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/pkg/errors"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
"github.com/zeromicro/go-zero/core/logx"
)
type CheckOrderAgentLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCheckOrderAgentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckOrderAgentLogic {
return &CheckOrderAgentLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CheckOrderAgentLogic) CheckOrderAgent(req *types.CheckOrderAgentReq) (resp *types.CheckOrderAgentResp, err error) {
// 获取当前用户ID
userId, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err)
}
// 检查订单是否属于当前代理推广
isAgent, err := query.IsOrderAgent(l.ctx, l.svcCtx, userId, req.OrderId)
if err != nil {
return nil, err
}
return &types.CheckOrderAgentResp{
IsAgentOrder: isAgent,
}, nil
}

View File

@@ -36,7 +36,7 @@ func (l *QuerySingleTestLogic) QuerySingleTest(req *types.QuerySingleTestReq) (r
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "单查测试, 序列化参数失败 : %d", err)
}
apiResp, err := l.svcCtx.ApiRequestService.PreprocessRequestApi(marshalParams, req.Api)
apiResp, err := l.svcCtx.ApiRequestService.PreprocessRequestApi(l.ctx, marshalParams, req.Api)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "单查测试, 获取接口失败 : %d", err)
}

View File

@@ -158,7 +158,7 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
encryptData = encryptedEmptyData
} else {
// 正常模式调用API请求服务
combinedResponse, err := l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id)
combinedResponse, err := l.svcCtx.ApiRequestService.ProcessRequests(ctx, decryptData, product.Id)
if err != nil {
return l.handleError(ctx, err, order, query)
}

View File

@@ -32,6 +32,61 @@ func generateAuthDateRange() string {
return fmt.Sprintf("%s-%s", start, end)
}
// callTianyuanApiWithLog 调用天元API并记录日志
func (a *ApiRequestService) callTianyuanApiWithLog(ctx context.Context, featureID, apiID string, params map[string]interface{}) (*tianyuanapi.Response, error) {
startTime := time.Now()
resp, err := a.tianyuanapi.CallInterface(apiID, params)
responseTime := time.Since(startTime).Milliseconds()
// 如果没有提供featureID尝试从缓存中获取
if featureID == "" {
a.apiFeatureMapMutex.RLock()
featureID = a.apiFeatureMapCache[apiID]
a.apiFeatureMapMutex.RUnlock()
}
// 构建调用记录选项
callStatus := int64(0) // 默认失败
errorCode := ""
errorMessage := ""
responseData := interface{}(nil)
transactionID := ""
if err != nil {
// 调用失败
errorMessage = err.Error()
// 尝试从错误信息中提取错误码
if code := tianyuanapi.GetCodeByError(err); code != -1 {
errorCode = fmt.Sprintf("%d", code)
}
} else {
// 调用成功
callStatus = 1
responseData = resp.Data
transactionID = resp.TransactionID
}
// 异步记录调用日志(避免影响主流程)
go func() {
logOpts := CallLogOptions{
FeatureID: featureID,
ApiID: apiID,
CallStatus: callStatus,
ResponseTime: responseTime,
ErrorCode: errorCode,
ErrorMessage: errorMessage,
RequestParams: params,
ResponseData: responseData,
TransactionID: transactionID,
}
if recordErr := a.tianyuanapiCallLogService.RecordCall(context.Background(), logOpts); recordErr != nil {
logx.Errorf("记录天元API调用日志失败api_id=%s, err=%v", apiID, recordErr)
}
}()
return resp, err
}
type ApiRequestService struct {
config config.Config
featureModel model.FeatureModel
@@ -39,16 +94,22 @@ type ApiRequestService struct {
userFeatureWhitelistModel model.UserFeatureWhitelistModel
whitelistService *WhitelistService
tianyuanapi *tianyuanapi.Client
tianyuanapiCallLogService *TianyuanapiCallLogService
apiFeatureMapCache map[string]string // apiID -> featureID 缓存
apiFeatureMapMutex sync.RWMutex
}
// NewApiRequestService 是一个构造函数,用于初始化 ApiRequestService
func NewApiRequestService(c config.Config, featureModel model.FeatureModel, productFeatureModel model.ProductFeatureModel, userFeatureWhitelistModel model.UserFeatureWhitelistModel, tianyuanapi *tianyuanapi.Client) *ApiRequestService {
func NewApiRequestService(c config.Config, featureModel model.FeatureModel, productFeatureModel model.ProductFeatureModel, userFeatureWhitelistModel model.UserFeatureWhitelistModel, tianyuanapi *tianyuanapi.Client, tianyuanapiCallLogService *TianyuanapiCallLogService, whitelistService *WhitelistService) *ApiRequestService {
return &ApiRequestService{
config: c,
featureModel: featureModel,
productFeatureModel: productFeatureModel,
userFeatureWhitelistModel: userFeatureWhitelistModel,
tianyuanapi: tianyuanapi,
tianyuanapiCallLogService: tianyuanapiCallLogService,
whitelistService: whitelistService,
apiFeatureMapCache: make(map[string]string),
}
}
@@ -61,15 +122,21 @@ type APIResponseData struct {
}
// ProcessRequests 处理请求
func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]byte, error) {
var ctx, cancel = context.WithCancel(context.Background())
func (a *ApiRequestService) ProcessRequests(ctx context.Context, params []byte, productID string) ([]byte, error) {
var cancel context.CancelFunc
ctx, cancel = context.WithCancel(ctx)
defer cancel()
// 从params中提取id_card用于白名单检查
idCard := gjson.GetBytes(params, "id_card").String()
// 查询白名单如果提供了id_card集中由 WhitelistService 处理
whitelistedFeatureApiIds, _ := a.whitelistService.GetWhitelistedFeatureApisByIdCard(ctx, idCard)
var whitelistedFeatureApiIds map[string]bool
if a.whitelistService != nil {
whitelistedFeatureApiIds, _ = a.whitelistService.GetWhitelistedFeatureApisByIdCard(ctx, idCard)
} else {
whitelistedFeatureApiIds = make(map[string]bool)
}
build := a.productFeatureModel.SelectBuilder().Where(squirrel.Eq{
"product_id": productID,
@@ -95,6 +162,13 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]
if len(featureList) == 0 {
return nil, errors.New("处理请求错误,产品无对应接口功能")
}
// 缓存apiID到featureID的映射关系供后续调用记录使用
a.apiFeatureMapMutex.Lock()
for _, feature := range featureList {
a.apiFeatureMapCache[feature.ApiId] = feature.Id
}
a.apiFeatureMapMutex.Unlock()
var (
wg sync.WaitGroup
resultsCh = make(chan APIResponseData, len(featureList))
@@ -140,7 +214,7 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]
tryCount := 0
for {
tryCount++
resp, preprocessErr = a.PreprocessRequestApi(params, feature.ApiId)
resp, preprocessErr = a.PreprocessRequestApi(ctx, params, feature.ApiId)
if preprocessErr == nil {
break
}
@@ -197,7 +271,7 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]
}
// ------------------------------------请求处理器--------------------------
var requestProcessors = map[string]func(*ApiRequestService, []byte) ([]byte, error){
var requestProcessors = map[string]func(*ApiRequestService, context.Context, []byte) ([]byte, error){
"PersonEnterprisePro": (*ApiRequestService).ProcessPersonEnterpriseProRequest,
"BehaviorRiskScan": (*ApiRequestService).ProcessBehaviorRiskScanRequest,
"YYSYBE08": (*ApiRequestService).ProcessYYSYBE08Request,
@@ -232,16 +306,16 @@ var requestProcessors = map[string]func(*ApiRequestService, []byte) ([]byte, err
}
// PreprocessRequestApi 调用指定的请求处理函数
func (a *ApiRequestService) PreprocessRequestApi(params []byte, apiID string) ([]byte, error) {
func (a *ApiRequestService) PreprocessRequestApi(ctx context.Context, params []byte, apiID string) ([]byte, error) {
if processor, exists := requestProcessors[apiID]; exists {
return processor(a, params) // 调用 ApiRequestService 方法
return processor(a, ctx, params) // 调用 ApiRequestService 方法
}
return nil, errors.New("api请求, 未找到相应的处理程序")
}
// PersonEnterprisePro 人企业关系加强版
func (a *ApiRequestService) ProcessPersonEnterpriseProRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessPersonEnterpriseProRequest(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
// 设置最大调用次数上限
maxApiCalls := 20 // 允许最多查询20个企业
@@ -250,7 +324,7 @@ func (a *ApiRequestService) ProcessPersonEnterpriseProRequest(params []byte) ([]
return nil, errors.New("api请求, PersonEnterprisePro, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QYGLB4C0", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "QYGLB4C0", map[string]interface{}{
"id_card": idCard.String(),
})
if err != nil {
@@ -452,7 +526,7 @@ func (a *ApiRequestService) ProcessPersonEnterpriseProRequest(params []byte) ([]
}
// 调用QYGL8271接口获取企业涉诉信息
lawsuitResp, err := a.tianyuanapi.CallInterface("QYGL8271", map[string]interface{}{
lawsuitResp, err := a.callTianyuanApiWithLog(ctx, "", "QYGL8271", map[string]interface{}{
"ent_name": orgName.String(),
"ent_code": creditCode.String(),
"auth_date": generateAuthDateRange(),
@@ -576,14 +650,14 @@ func (a *ApiRequestService) ProcessPersonEnterpriseProRequest(params []byte) ([]
}
// ProcesFLXG0V4BRequest 个人司法涉诉(详版)
func (a *ApiRequestService) ProcessFLXG0V4BRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessFLXG0V4BRequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
if !name.Exists() || !idCard.Exists() {
return nil, errors.New("api请求, FLXG0V4B, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("FLXG0V4B", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "FLXG0V4B", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"auth_date": generateAuthDateRange(),
@@ -599,13 +673,13 @@ func (a *ApiRequestService) ProcessFLXG0V4BRequest(params []byte) ([]byte, error
}
// ProcessFLXG0687Request 反诈反赌核验
func (a *ApiRequestService) ProcessFLXG0687Request(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessFLXG0687Request(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
if !idCard.Exists() {
return nil, errors.New("api请求, FLXG0687, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("FLXG0687", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "FLXG0687", map[string]interface{}{
"id_card": idCard.String(),
})
@@ -627,7 +701,7 @@ func (a *ApiRequestService) ProcessFLXG0687Request(params []byte) ([]byte, error
}
// ProcessFLXG3D56Request 违约失信
func (a *ApiRequestService) ProcessFLXG3D56Request(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessFLXG3D56Request(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -635,7 +709,7 @@ func (a *ApiRequestService) ProcessFLXG3D56Request(params []byte) ([]byte, error
return nil, errors.New("api请求, FLXG3D56, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("FLXG3D56", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "FLXG3D56", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -682,14 +756,14 @@ func (a *ApiRequestService) ProcessFLXG3D56Request(params []byte) ([]byte, error
}
// ProcessIVYZ5733Request 婚姻状况
func (a *ApiRequestService) ProcessIVYZ5733Request(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessIVYZ5733Request(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
if !idCard.Exists() || !name.Exists() {
return nil, errors.New("api请求, IVYZ5733, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("IVYZ5733", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "IVYZ5733", map[string]interface{}{
"id_card": idCard.String(),
"name": name.String(),
})
@@ -738,14 +812,14 @@ func (a *ApiRequestService) ProcessIVYZ5733Request(params []byte) ([]byte, error
}
// ProcessIVYZ9A2BRequest 学历查询
func (a *ApiRequestService) ProcessIVYZ9A2BRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessIVYZ9A2BRequest(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
if !idCard.Exists() || !name.Exists() {
return nil, errors.New("api请求, IVYZ9A2B, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("IVYZ9A2B", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "IVYZ9A2B", map[string]interface{}{
"id_card": idCard.String(),
"name": name.String(),
})
@@ -809,14 +883,14 @@ func (a *ApiRequestService) ProcessIVYZ9A2BRequest(params []byte) ([]byte, error
}
// ProcessYYSYBE08Request 二要素
func (a *ApiRequestService) ProcessYYSYBE08Request(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessYYSYBE08Request(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
if !name.Exists() || !idCard.Exists() {
return nil, errors.New("api请求, YYSYBE08, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("YYSYBE08", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "YYSYBE08", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
})
@@ -850,7 +924,7 @@ func (a *ApiRequestService) ProcessYYSYBE08Request(params []byte) ([]byte, error
}
// ProcessJRZQ0A03Request 借贷申请
func (a *ApiRequestService) ProcessJRZQ0A03Request(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessJRZQ0A03Request(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -858,7 +932,7 @@ func (a *ApiRequestService) ProcessJRZQ0A03Request(params []byte) ([]byte, error
return nil, errors.New("api请求, JRZQ0A03, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("JRZQ0A03", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "JRZQ0A03", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -907,7 +981,7 @@ func (a *ApiRequestService) ProcessJRZQ0A03Request(params []byte) ([]byte, error
}
// ProcessJRZQ8203Request 借贷行为
func (a *ApiRequestService) ProcessJRZQ8203Request(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessJRZQ8203Request(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -915,7 +989,7 @@ func (a *ApiRequestService) ProcessJRZQ8203Request(params []byte) ([]byte, error
return nil, errors.New("api请求, JRZQ8203, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("JRZQ8203", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "JRZQ8203", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -964,7 +1038,7 @@ func (a *ApiRequestService) ProcessJRZQ8203Request(params []byte) ([]byte, error
}
// ProcessJRZQ4AA8Request 还款压力
func (a *ApiRequestService) ProcessJRZQ4AA8Request(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessJRZQ4AA8Request(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
mobile := gjson.GetBytes(params, "mobile")
@@ -972,7 +1046,7 @@ func (a *ApiRequestService) ProcessJRZQ4AA8Request(params []byte) ([]byte, error
return nil, errors.New("api请求, JRZQ4AA8, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("JRZQ4AA8", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "JRZQ4AA8", map[string]interface{}{
"id_card": idCard.String(),
"name": name.String(),
"mobile_no": mobile.String(),
@@ -1013,7 +1087,7 @@ func (a *ApiRequestService) ProcessJRZQ4AA8Request(params []byte) ([]byte, error
}
// ProcessQYGL8271Request 企业涉诉
func (a *ApiRequestService) ProcessQYGL8271Request(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessQYGL8271Request(ctx context.Context, params []byte) ([]byte, error) {
entName := gjson.GetBytes(params, "ent_name")
entCode := gjson.GetBytes(params, "ent_code")
@@ -1021,7 +1095,7 @@ func (a *ApiRequestService) ProcessQYGL8271Request(params []byte) ([]byte, error
return nil, errors.New("api请求, QYGL8271, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QYGL8271", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "QYGL8271", map[string]interface{}{
"ent_name": entName.String(),
"ent_code": entCode.String(),
})
@@ -1073,13 +1147,13 @@ func (a *ApiRequestService) ProcessQYGL8271Request(params []byte) ([]byte, error
}
// ProcessQYGL6F2DRequest 人企关联
func (a *ApiRequestService) ProcessQYGL6F2DRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessQYGL6F2DRequest(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
if !idCard.Exists() {
return nil, errors.New("api请求, QYGL6F2D, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QYGL6F2D", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "QYGL6F2D", map[string]interface{}{
"id_card": idCard.String(),
})
@@ -1114,13 +1188,13 @@ func (a *ApiRequestService) ProcessQYGL6F2DRequest(params []byte) ([]byte, error
}
// ProcessQCXG7A2BRequest 名下车辆
func (a *ApiRequestService) ProcessQCXG7A2BRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessQCXG7A2BRequest(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
if !idCard.Exists() {
return nil, errors.New("api请求, QCXG7A2B, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QCXG7A2B", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "QCXG7A2B", map[string]interface{}{
"id_card": idCard.String(),
})
@@ -1132,7 +1206,7 @@ func (a *ApiRequestService) ProcessQCXG7A2BRequest(params []byte) ([]byte, error
}
// ProcessYYSY09CDRequest 三要素
func (a *ApiRequestService) ProcessYYSY09CDRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessYYSY09CDRequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -1140,7 +1214,7 @@ func (a *ApiRequestService) ProcessYYSY09CDRequest(params []byte) ([]byte, error
return nil, errors.New("api请求, YYSY09CD, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("YYSY09CD", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "YYSY09CD", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -1175,7 +1249,7 @@ func (a *ApiRequestService) ProcessYYSY09CDRequest(params []byte) ([]byte, error
}
// ProcessBehaviorRiskScanRequest 行为风险扫描
func (a *ApiRequestService) ProcessBehaviorRiskScanRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessBehaviorRiskScanRequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
@@ -1197,7 +1271,7 @@ func (a *ApiRequestService) ProcessBehaviorRiskScanRequest(params []byte) ([]byt
// 反赌反诈
go func() {
defer wg.Done()
respBytes, err := a.ProcessFLXG0687Request(params)
respBytes, err := a.ProcessFLXG0687Request(ctx, params)
results <- apiResult{name: "anti_fraud_gaming", data: respBytes, err: err}
}()
@@ -1242,7 +1316,7 @@ func (a *ApiRequestService) ProcessBehaviorRiskScanRequest(params []byte) ([]byt
}
// ProcessDWBG8B4DRequest 谛听多维报告
func (a *ApiRequestService) ProcessDWBG8B4DRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessDWBG8B4DRequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -1251,7 +1325,7 @@ func (a *ApiRequestService) ProcessDWBG8B4DRequest(params []byte) ([]byte, error
return nil, errors.New("api请求, DWBG8B4D, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("DWBG8B4D", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "DWBG8B4D", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -1267,7 +1341,7 @@ func (a *ApiRequestService) ProcessDWBG8B4DRequest(params []byte) ([]byte, error
}
// ProcessDWBG6A2CRequest 司南报告服务
func (a *ApiRequestService) ProcessDWBG6A2CRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessDWBG6A2CRequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -1276,7 +1350,7 @@ func (a *ApiRequestService) ProcessDWBG6A2CRequest(params []byte) ([]byte, error
return nil, errors.New("api请求, DWBG6A2C, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("DWBG6A2C", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "DWBG6A2C", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -1292,7 +1366,7 @@ func (a *ApiRequestService) ProcessDWBG6A2CRequest(params []byte) ([]byte, error
}
// ProcessJRZQ4B6CRequest 探针C风险评估
func (a *ApiRequestService) ProcessJRZQ4B6CRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessJRZQ4B6CRequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -1300,7 +1374,7 @@ func (a *ApiRequestService) ProcessJRZQ4B6CRequest(params []byte) ([]byte, error
return nil, errors.New("api请求, JRZQ4B6C, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("JRZQ4B6C", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "JRZQ4B6C", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -1316,7 +1390,7 @@ func (a *ApiRequestService) ProcessJRZQ4B6CRequest(params []byte) ([]byte, error
}
// ProcessJRZQ09J8Request 收入评估
func (a *ApiRequestService) ProcessJRZQ09J8Request(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessJRZQ09J8Request(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -1324,7 +1398,7 @@ func (a *ApiRequestService) ProcessJRZQ09J8Request(params []byte) ([]byte, error
return nil, errors.New("api请求, JRZQ09J8, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("JRZQ09J8", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "JRZQ09J8", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -1340,7 +1414,7 @@ func (a *ApiRequestService) ProcessJRZQ09J8Request(params []byte) ([]byte, error
}
// ProcessJRZQ5E9FRequest 借选指数
func (a *ApiRequestService) ProcessJRZQ5E9FRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessJRZQ5E9FRequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -1348,7 +1422,7 @@ func (a *ApiRequestService) ProcessJRZQ5E9FRequest(params []byte) ([]byte, error
return nil, errors.New("api请求, JRZQ5E9F, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("JRZQ5E9F", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "JRZQ5E9F", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -1364,13 +1438,13 @@ func (a *ApiRequestService) ProcessJRZQ5E9FRequest(params []byte) ([]byte, error
}
// ProcessQYGL3F8ERequest 人企关系加强版2
func (a *ApiRequestService) ProcessQYGL3F8ERequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessQYGL3F8ERequest(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
if !idCard.Exists() {
return nil, errors.New("api请求, QYGL3F8E, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QYGL3F8E", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "QYGL3F8E", map[string]interface{}{
"id_card": idCard.String(),
})
@@ -1383,14 +1457,14 @@ func (a *ApiRequestService) ProcessQYGL3F8ERequest(params []byte) ([]byte, error
}
// ProcessIVYZ81NCRequest 婚姻,登记时间版
func (a *ApiRequestService) ProcessIVYZ81NCRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessIVYZ81NCRequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
if !name.Exists() || !idCard.Exists() {
return nil, errors.New("api请求, IVYZ81NC, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("IVYZ81NC", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "IVYZ81NC", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
})
@@ -1404,14 +1478,14 @@ func (a *ApiRequestService) ProcessIVYZ81NCRequest(params []byte) ([]byte, error
}
// ProcessIVYZ7F3ARequest 学历查询版B
func (a *ApiRequestService) ProcessIVYZ7F3ARequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessIVYZ7F3ARequest(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
if !idCard.Exists() || !name.Exists() {
return nil, errors.New("api请求, IVYZ7F3A, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("IVYZ7F3A", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "IVYZ7F3A", map[string]interface{}{
"id_card": idCard.String(),
"name": name.String(),
"authorized": "1",
@@ -1426,7 +1500,7 @@ func (a *ApiRequestService) ProcessIVYZ7F3ARequest(params []byte) ([]byte, error
}
// ProcessDWBG7F3ARequest 多头借贷行业风险版
func (a *ApiRequestService) ProcessDWBG7F3ARequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessDWBG7F3ARequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -1434,7 +1508,7 @@ func (a *ApiRequestService) ProcessDWBG7F3ARequest(params []byte) ([]byte, error
return nil, errors.New("api请求, DWBG7F3A, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("DWBG7F3A", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "DWBG7F3A", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -1449,7 +1523,7 @@ func (a *ApiRequestService) ProcessDWBG7F3ARequest(params []byte) ([]byte, error
}
// ProcessJRZQ8A2DRequest 特殊名单验证B
func (a *ApiRequestService) ProcessJRZQ8A2DRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessJRZQ8A2DRequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -1457,7 +1531,7 @@ func (a *ApiRequestService) ProcessJRZQ8A2DRequest(params []byte) ([]byte, error
return nil, errors.New("api请求, JRZQ8A2D, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("JRZQ8A2D", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "JRZQ8A2D", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -1473,13 +1547,13 @@ func (a *ApiRequestService) ProcessJRZQ8A2DRequest(params []byte) ([]byte, error
}
// ProcessYYSY8B1CRequest 手机在网时长B
func (a *ApiRequestService) ProcessYYSY8B1CRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessYYSY8B1CRequest(ctx context.Context, params []byte) ([]byte, error) {
mobile := gjson.GetBytes(params, "mobile")
if !mobile.Exists() {
return nil, errors.New("api请求, YYSY8B1C, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("YYSY8B1C", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "YYSY8B1C", map[string]interface{}{
"mobile_no": mobile.String(),
})
@@ -1492,13 +1566,13 @@ func (a *ApiRequestService) ProcessYYSY8B1CRequest(params []byte) ([]byte, error
}
// ProcessYYSY7D3ERequest 携号转网查询
func (a *ApiRequestService) ProcessYYSY7D3ERequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessYYSY7D3ERequest(ctx context.Context, params []byte) ([]byte, error) {
mobile := gjson.GetBytes(params, "mobile")
if !mobile.Exists() {
return nil, errors.New("api请求, YYSY7D3E, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("YYSY7D3E", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "YYSY7D3E", map[string]interface{}{
"mobile_no": mobile.String(),
})
@@ -1511,7 +1585,7 @@ func (a *ApiRequestService) ProcessYYSY7D3ERequest(params []byte) ([]byte, error
}
// ProcessFLXG7E8FRequest 个人司法涉诉查询
func (a *ApiRequestService) ProcessFLXG7E8FRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessFLXG7E8FRequest(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
mobile := gjson.GetBytes(params, "mobile")
@@ -1519,7 +1593,7 @@ func (a *ApiRequestService) ProcessFLXG7E8FRequest(params []byte) ([]byte, error
return nil, errors.New("api请求, FLXG7E8F, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("FLXG7E8F", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "FLXG7E8F", map[string]interface{}{
"id_card": idCard.String(),
"name": name.String(),
"mobile_no": mobile.String(),
@@ -1534,7 +1608,7 @@ func (a *ApiRequestService) ProcessFLXG7E8FRequest(params []byte) ([]byte, error
}
// ProcessIVYZ8I9JRequest 互联网行为推测
func (a *ApiRequestService) ProcessIVYZ8I9JRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessIVYZ8I9JRequest(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
mobile := gjson.GetBytes(params, "mobile")
@@ -1542,7 +1616,7 @@ func (a *ApiRequestService) ProcessIVYZ8I9JRequest(params []byte) ([]byte, error
return nil, errors.New("api请求, IVYZ8I9J, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("IVYZ8I9J", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "IVYZ8I9J", map[string]interface{}{
"id_card": idCard.String(),
"name": name.String(),
"mobile_no": mobile.String(),
@@ -1557,7 +1631,7 @@ func (a *ApiRequestService) ProcessIVYZ8I9JRequest(params []byte) ([]byte, error
}
// ProcessJRZQ7F1ARequest 全景雷达
func (a *ApiRequestService) ProcessJRZQ7F1ARequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessJRZQ7F1ARequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -1565,7 +1639,7 @@ func (a *ApiRequestService) ProcessJRZQ7F1ARequest(params []byte) ([]byte, error
return nil, errors.New("api请求, JRZQ7F1A, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("JRZQ7F1A", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "JRZQ7F1A", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),
@@ -1581,14 +1655,14 @@ func (a *ApiRequestService) ProcessJRZQ7F1ARequest(params []byte) ([]byte, error
}
// ProcessIVYZ3P9MRequest 学历实时查询
func (a *ApiRequestService) ProcessIVYZ3P9MRequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessIVYZ3P9MRequest(ctx context.Context, params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
if !idCard.Exists() || !name.Exists() {
return nil, errors.New("api请求, IVYZ3P9M, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("IVYZ3P9M", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "IVYZ3P9M", map[string]interface{}{
"id_card": idCard.String(),
"name": name.String(),
})
@@ -1601,7 +1675,7 @@ func (a *ApiRequestService) ProcessIVYZ3P9MRequest(params []byte) ([]byte, error
}
// ProcessJRZQ6F2ARequest 借贷申请
func (a *ApiRequestService) ProcessJRZQ6F2ARequest(params []byte) ([]byte, error) {
func (a *ApiRequestService) ProcessJRZQ6F2ARequest(ctx context.Context, params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")
idCard := gjson.GetBytes(params, "id_card")
mobile := gjson.GetBytes(params, "mobile")
@@ -1609,7 +1683,7 @@ func (a *ApiRequestService) ProcessJRZQ6F2ARequest(params []byte) ([]byte, error
return nil, errors.New("api请求, JRZQ6F2A, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("JRZQ6F2A", map[string]interface{}{
resp, err := a.callTianyuanApiWithLog(ctx, "", "JRZQ6F2A", map[string]interface{}{
"name": name.String(),
"id_card": idCard.String(),
"mobile_no": mobile.String(),

View File

@@ -0,0 +1,201 @@
package service
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"time"
"ycc-server/app/main/model"
"github.com/Masterminds/squirrel"
"github.com/zeromicro/go-zero/core/logx"
)
// TianyuanapiCallLogService 天元API调用记录服务
type TianyuanapiCallLogService struct {
tianyuanapiCallLogModel model.TianyuanapiCallLogModel
featureModel model.FeatureModel
}
// NewTianyuanapiCallLogService 创建天元API调用记录服务
func NewTianyuanapiCallLogService(
tianyuanapiCallLogModel model.TianyuanapiCallLogModel,
featureModel model.FeatureModel,
) *TianyuanapiCallLogService {
return &TianyuanapiCallLogService{
tianyuanapiCallLogModel: tianyuanapiCallLogModel,
featureModel: featureModel,
}
}
// CallLogOptions 调用记录选项
type CallLogOptions struct {
FeatureID string // 功能ID
ApiID string // API标识YYSYBE08
OrderID string // 订单ID可选
QueryID string // 查询ID可选
CallStatus int64 // 调用状态0=失败1=成功
ResponseTime int64 // 响应耗时(毫秒)
ErrorCode string // 错误码(失败时)
ErrorMessage string // 错误信息(失败时)
RequestParams interface{} // 请求参数(可选)
ResponseData interface{} // 响应数据(可选)
TransactionID string // 天元API流水号
}
// RecordCall 记录天元API调用
func (s *TianyuanapiCallLogService) RecordCall(ctx context.Context, opts CallLogOptions) error {
// 1. 获取feature的成本价
costPrice := 0.00
if opts.CallStatus == 1 { // 只有成功才计算成本
feature, err := s.featureModel.FindOne(ctx, opts.FeatureID)
if err == nil {
costPrice = feature.CostPrice
} else {
logx.Errorf("查询feature成本价失败feature_id=%s, err=%v", opts.FeatureID, err)
}
}
// 2. 转换参数和响应为JSON字符串
var requestParamsStr, responseDataStr *string
if opts.RequestParams != nil {
if bytes, err := json.Marshal(opts.RequestParams); err == nil {
jsonStr := string(bytes)
requestParamsStr = &jsonStr
}
}
if opts.ResponseData != nil {
// 只记录响应数据的前1000个字符避免存储过大
if bytes, err := json.Marshal(opts.ResponseData); err == nil {
jsonStr := string(bytes)
if len(jsonStr) > 1000 {
jsonStr = jsonStr[:1000] + "...[truncated]"
}
responseDataStr = &jsonStr
}
}
// 3. 构建调用记录
callTime := time.Now()
deleteTime := sql.NullTime{}
callLog := &model.TianyuanapiCallLog{
FeatureId: opts.FeatureID,
ApiId: opts.ApiID,
OrderId: sql.NullString{},
QueryId: sql.NullString{},
CallStatus: opts.CallStatus,
CallTime: callTime,
ResponseTime: sql.NullInt64{},
CostPrice: costPrice,
ErrorCode: sql.NullString{},
ErrorMessage: sql.NullString{},
RequestParams: sql.NullString{},
ResponseData: sql.NullString{},
TransactionId: sql.NullString{},
CreateTime: callTime,
UpdateTime: callTime,
DeleteTime: deleteTime,
DelState: 0,
Version: 0,
}
// 4. 填充可选字段
if opts.OrderID != "" {
callLog.OrderId = sql.NullString{String: opts.OrderID, Valid: true}
}
if opts.QueryID != "" {
callLog.QueryId = sql.NullString{String: opts.QueryID, Valid: true}
}
if opts.ResponseTime > 0 {
callLog.ResponseTime = sql.NullInt64{Int64: opts.ResponseTime, Valid: true}
}
if opts.ErrorCode != "" {
callLog.ErrorCode = sql.NullString{String: opts.ErrorCode, Valid: true}
}
if opts.ErrorMessage != "" {
callLog.ErrorMessage = sql.NullString{String: opts.ErrorMessage, Valid: true}
}
if requestParamsStr != nil {
callLog.RequestParams = sql.NullString{String: *requestParamsStr, Valid: true}
}
if responseDataStr != nil {
callLog.ResponseData = sql.NullString{String: *responseDataStr, Valid: true}
}
if opts.TransactionID != "" {
callLog.TransactionId = sql.NullString{String: opts.TransactionID, Valid: true}
}
// 5. 插入记录
_, err := s.tianyuanapiCallLogModel.Insert(ctx, nil, callLog)
if err != nil {
logx.Errorf("插入天元API调用记录失败feature_id=%s, api_id=%s, err=%v", opts.FeatureID, opts.ApiID, err)
return fmt.Errorf("插入调用记录失败: %w", err)
}
return nil
}
// GetStatistics 获取统计信息
type StatisticsFilter struct {
FeatureID string // 功能ID
ApiID string // API标识
StartDate time.Time // 开始日期
EndDate time.Time // 结束日期
}
// Statistics 统计信息
type Statistics struct {
TotalCalls int64 // 总调用次数
SuccessCalls int64 // 成功次数
FailedCalls int64 // 失败次数
TotalCost float64 // 总成本(成功调用的成本之和)
}
// GetStatistics 获取统计信息
func (s *TianyuanapiCallLogService) GetStatistics(ctx context.Context, filter StatisticsFilter) (*Statistics, error) {
builder := s.tianyuanapiCallLogModel.SelectBuilder()
// 添加过滤条件
if filter.FeatureID != "" {
builder = builder.Where(squirrel.Eq{"feature_id": filter.FeatureID})
}
if filter.ApiID != "" {
builder = builder.Where(squirrel.Eq{"api_id": filter.ApiID})
}
if !filter.StartDate.IsZero() {
builder = builder.Where(squirrel.GtOrEq{"call_time": filter.StartDate})
}
if !filter.EndDate.IsZero() {
builder = builder.Where(squirrel.Lt{"call_time": filter.EndDate})
}
// 统计总调用次数
totalCalls, err := s.tianyuanapiCallLogModel.FindCount(ctx, builder, "id")
if err != nil {
return nil, fmt.Errorf("统计总调用次数失败: %w", err)
}
// 统计成功次数
successBuilder := builder.Where(squirrel.Eq{"call_status": 1})
successCalls, err := s.tianyuanapiCallLogModel.FindCount(ctx, successBuilder, "id")
if err != nil {
return nil, fmt.Errorf("统计成功次数失败: %w", err)
}
// 统计失败次数
failedCalls := totalCalls - successCalls
// 统计总成本(仅成功调用)
totalCost, err := s.tianyuanapiCallLogModel.FindSum(ctx, successBuilder, "cost_price")
if err != nil {
return nil, fmt.Errorf("统计总成本失败: %w", err)
}
return &Statistics{
TotalCalls: totalCalls,
SuccessCalls: successCalls,
FailedCalls: failedCalls,
TotalCost: totalCost,
}, nil
}

View File

@@ -134,6 +134,9 @@ func (s *WhitelistService) GetWhitelistedFeatureApisByIdCard(
idCard string,
) (map[string]bool, error) {
result := make(map[string]bool)
if s == nil {
return result, nil
}
if idCard == "" {
return result, nil
}

View File

@@ -34,6 +34,10 @@ type ServiceContext struct {
FeatureModel model.FeatureModel
ProductFeatureModel model.ProductFeatureModel
// 天元API调用记录模型
TianyuanapiCallLogModel model.TianyuanapiCallLogModel
TianyuanapiCallLogService *service.TianyuanapiCallLogService
// 白名单相关模型
UserFeatureWhitelistModel model.UserFeatureWhitelistModel
WhitelistOrderModel model.WhitelistOrderModel
@@ -128,6 +132,9 @@ func NewServiceContext(c config.Config) *ServiceContext {
featureModel := model.NewFeatureModel(db, cacheConf)
productFeatureModel := model.NewProductFeatureModel(db, cacheConf)
// ============================== 天元API调用记录模型 ==============================
tianyuanapiCallLogModel := model.NewTianyuanapiCallLogModel(db, cacheConf)
// ============================== 白名单相关模型 ==============================
userFeatureWhitelistModel := model.NewUserFeatureWhitelistModel(db, cacheConf)
whitelistOrderModel := model.NewWhitelistOrderModel(db, cacheConf)
@@ -196,8 +203,10 @@ func NewServiceContext(c config.Config) *ServiceContext {
alipayService := service.NewAliPayService(c)
wechatPayService := service.NewWechatPayService(c, userAuthModel, service.InitTypeWxPayPubKey)
applePayService := service.NewApplePayService(c)
apiRequestService := service.NewApiRequestService(c, featureModel, productFeatureModel, userFeatureWhitelistModel, tianyuanapi)
tianyuanapiCallLogService := service.NewTianyuanapiCallLogService(tianyuanapiCallLogModel, featureModel)
// 注意whitelistService 需要在 apiRequestService 之前创建,因为 apiRequestService 依赖它
whitelistService := service.NewWhitelistService(c, userFeatureWhitelistModel, whitelistOrderModel, whitelistOrderItemModel, queryModel, featureModel)
apiRequestService := service.NewApiRequestService(c, featureModel, productFeatureModel, userFeatureWhitelistModel, tianyuanapi, tianyuanapiCallLogService, whitelistService)
verificationService := service.NewVerificationService(c, tianyuanapi, apiRequestService)
asynqService := service.NewAsynqService(c)
agentService := service.NewAgentService(c, orderModel, agentModel, agentWalletModel,

View File

@@ -80,6 +80,14 @@ type CheckFeatureWhitelistStatusResp struct {
DataDeleted bool `json:"data_deleted"` // 报告数据是否已删除仅当提供了query_id时有效
}
type CheckOrderAgentReq struct {
OrderId string `form:"order_id"` // 订单ID
}
type CheckOrderAgentResp struct {
IsAgentOrder bool `json:"is_agent_order"` // 是否是当前代理推广的订单
}
type ConversionRateResp struct {
MyConversionRate ConversionRateData `json:"my_conversion_rate"` // 我的转化率
SubordinateConversionRate ConversionRateData `json:"subordinate_conversion_rate"` // 我的下级转化率

View File

@@ -37,6 +37,17 @@ type AdminOrderStatistics struct {
ChangeRate float64 `json:"change_rate"` // 变化率(百分比)
}
type AdminProfitDetail struct {
Revenue float64 `json:"revenue"` // 营收
Commission float64 `json:"commission"` // 佣金
Rebate float64 `json:"rebate"` // 返利
CompanyTax float64 `json:"company_tax"` // 税务成本
ApiCost float64 `json:"api_cost"` // API调用成本
TaxIncome float64 `json:"tax_income"` // 提现收税
Profit float64 `json:"profit"` // 利润
ProfitRate float64 `json:"profit_rate"` // 利润率
}
type AdminProfitStatistics struct {
TodayProfit float64 `json:"today_profit"` // 今日利润
MonthProfit float64 `json:"month_profit"` // 当月利润
@@ -44,6 +55,9 @@ type AdminProfitStatistics struct {
TodayProfitRate float64 `json:"today_profit_rate"` // 今日利润率
MonthProfitRate float64 `json:"month_profit_rate"` // 当月利润率
TotalProfitRate float64 `json:"total_profit_rate"` // 总利润率
TodayDetail AdminProfitDetail `json:"today_detail"`
MonthDetail AdminProfitDetail `json:"month_detail"`
TotalDetail AdminProfitDetail `json:"total_detail"`
}
type AdminQueryItem struct {

View File

@@ -67,6 +67,7 @@ type (
ApiId string `db:"api_id"` // API标识
Name string `db:"name"` // 描述
WhitelistPrice float64 `db:"whitelist_price"` // 白名单屏蔽价格(单位:元)
CostPrice float64 `db:"cost_price"` // 天远API调用成本价单位
}
)
@@ -83,11 +84,11 @@ func (m *defaultFeatureModel) Insert(ctx context.Context, session sqlx.Session,
yccFeatureApiIdKey := fmt.Sprintf("%s%v", cacheYccFeatureApiIdPrefix, data.ApiId)
yccFeatureIdKey := fmt.Sprintf("%s%v", cacheYccFeatureIdPrefix, data.Id)
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?)", m.table, featureRowsExpectAutoSet)
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?)", m.table, featureRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ApiId, data.Name, data.WhitelistPrice)
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ApiId, data.Name, data.WhitelistPrice, data.CostPrice)
}
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ApiId, data.Name, data.WhitelistPrice)
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ApiId, data.Name, data.WhitelistPrice, data.CostPrice)
}, yccFeatureApiIdKey, yccFeatureIdKey)
}
func (m *defaultFeatureModel) insertUUID(data *Feature) {
@@ -154,9 +155,9 @@ func (m *defaultFeatureModel) Update(ctx context.Context, session sqlx.Session,
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, featureRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.Id)
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.CostPrice, newData.Id)
}
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.Id)
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.CostPrice, newData.Id)
}, yccFeatureApiIdKey, yccFeatureIdKey)
}
@@ -177,9 +178,9 @@ func (m *defaultFeatureModel) UpdateWithVersion(ctx context.Context, session sql
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, featureRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.Id, oldVersion)
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.CostPrice, newData.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.Id, oldVersion)
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.CostPrice, newData.Id, oldVersion)
}, yccFeatureApiIdKey, yccFeatureIdKey)
if err != nil {
return err

View File

@@ -0,0 +1,27 @@
package model
import (
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ TianyuanapiCallLogModel = (*customTianyuanapiCallLogModel)(nil)
type (
// TianyuanapiCallLogModel is an interface to be customized, add more methods here,
// and implement the added methods in customTianyuanapiCallLogModel.
TianyuanapiCallLogModel interface {
tianyuanapiCallLogModel
}
customTianyuanapiCallLogModel struct {
*defaultTianyuanapiCallLogModel
}
)
// NewTianyuanapiCallLogModel returns a model for the database table.
func NewTianyuanapiCallLogModel(conn sqlx.SqlConn, c cache.CacheConf) TianyuanapiCallLogModel {
return &customTianyuanapiCallLogModel{
defaultTianyuanapiCallLogModel: newTianyuanapiCallLogModel(conn, c),
}
}

View File

@@ -0,0 +1,398 @@
// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"reflect"
"time"
"github.com/Masterminds/squirrel"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlc"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
"ycc-server/common/globalkey"
)
var (
tianyuanapiCallLogFieldNames = builder.RawFieldNames(&TianyuanapiCallLog{})
tianyuanapiCallLogRows = strings.Join(tianyuanapiCallLogFieldNames, ",")
tianyuanapiCallLogRowsExpectAutoSet = strings.Join(stringx.Remove(tianyuanapiCallLogFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
tianyuanapiCallLogRowsWithPlaceHolder = strings.Join(stringx.Remove(tianyuanapiCallLogFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheYccTianyuanapiCallLogIdPrefix = "cache:ycc:tianyuanapiCallLog:id:"
)
type (
tianyuanapiCallLogModel interface {
Insert(ctx context.Context, session sqlx.Session, data *TianyuanapiCallLog) (sql.Result, error)
FindOne(ctx context.Context, id int64) (*TianyuanapiCallLog, error)
Update(ctx context.Context, session sqlx.Session, data *TianyuanapiCallLog) (sql.Result, error)
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *TianyuanapiCallLog) error
Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error
SelectBuilder() squirrel.SelectBuilder
DeleteSoft(ctx context.Context, session sqlx.Session, data *TianyuanapiCallLog) error
FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error)
FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error)
FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*TianyuanapiCallLog, error)
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*TianyuanapiCallLog, error)
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*TianyuanapiCallLog, int64, error)
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*TianyuanapiCallLog, error)
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*TianyuanapiCallLog, error)
Delete(ctx context.Context, session sqlx.Session, id int64) error
}
defaultTianyuanapiCallLogModel struct {
sqlc.CachedConn
table string
}
TianyuanapiCallLog struct {
Id int64 `db:"id"` // 主键ID
FeatureId string `db:"feature_id"` // 功能ID关联feature表
ApiId string `db:"api_id"` // API标识YYSYBE08
OrderId sql.NullString `db:"order_id"` // 订单ID关联order表
QueryId sql.NullString `db:"query_id"` // 查询ID关联query表
CallStatus int64 `db:"call_status"` // 调用状态0=失败1=成功
CallTime time.Time `db:"call_time"` // 调用时间
ResponseTime sql.NullInt64 `db:"response_time"` // 响应耗时(毫秒)
CostPrice float64 `db:"cost_price"` // 本次调用成本成功时从feature.cost_price获取失败时为0
ErrorCode sql.NullString `db:"error_code"` // 错误码(失败时记录)
ErrorMessage sql.NullString `db:"error_message"` // 错误信息(失败时记录)
RequestParams sql.NullString `db:"request_params"` // 请求参数JSON格式
ResponseData sql.NullString `db:"response_data"` // 响应数据JSON格式仅记录关键信息
TransactionId sql.NullString `db:"transaction_id"` // 天远API流水号
CreateTime time.Time `db:"create_time"` // 创建时间
UpdateTime time.Time `db:"update_time"` // 更新时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"` // 删除状态0=未删除1=已删除
Version int64 `db:"version"` // 版本号(乐观锁)
}
)
func newTianyuanapiCallLogModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultTianyuanapiCallLogModel {
return &defaultTianyuanapiCallLogModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`tianyuanapi_call_log`",
}
}
func (m *defaultTianyuanapiCallLogModel) Insert(ctx context.Context, session sqlx.Session, data *TianyuanapiCallLog) (sql.Result, error) {
data.DelState = globalkey.DelStateNo
m.insertUUID(data)
yccTianyuanapiCallLogIdKey := fmt.Sprintf("%s%v", cacheYccTianyuanapiCallLogIdPrefix, data.Id)
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, tianyuanapiCallLogRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.FeatureId, data.ApiId, data.OrderId, data.QueryId, data.CallStatus, data.CallTime, data.ResponseTime, data.CostPrice, data.ErrorCode, data.ErrorMessage, data.RequestParams, data.ResponseData, data.TransactionId, data.DeleteTime, data.DelState, data.Version)
}
return conn.ExecCtx(ctx, query, data.FeatureId, data.ApiId, data.OrderId, data.QueryId, data.CallStatus, data.CallTime, data.ResponseTime, data.CostPrice, data.ErrorCode, data.ErrorMessage, data.RequestParams, data.ResponseData, data.TransactionId, data.DeleteTime, data.DelState, data.Version)
}, yccTianyuanapiCallLogIdKey)
}
func (m *defaultTianyuanapiCallLogModel) insertUUID(data *TianyuanapiCallLog) {
t := reflect.TypeOf(data).Elem()
v := reflect.ValueOf(data).Elem()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Tag.Get("db") == "id" {
f := v.Field(i)
if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
if f.String() == "" {
f.SetString(uuid.NewString())
}
}
break
}
}
}
func (m *defaultTianyuanapiCallLogModel) FindOne(ctx context.Context, id int64) (*TianyuanapiCallLog, error) {
yccTianyuanapiCallLogIdKey := fmt.Sprintf("%s%v", cacheYccTianyuanapiCallLogIdPrefix, id)
var resp TianyuanapiCallLog
err := m.QueryRowCtx(ctx, &resp, yccTianyuanapiCallLogIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", tianyuanapiCallLogRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultTianyuanapiCallLogModel) Update(ctx context.Context, session sqlx.Session, data *TianyuanapiCallLog) (sql.Result, error) {
yccTianyuanapiCallLogIdKey := fmt.Sprintf("%s%v", cacheYccTianyuanapiCallLogIdPrefix, data.Id)
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, tianyuanapiCallLogRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.FeatureId, data.ApiId, data.OrderId, data.QueryId, data.CallStatus, data.CallTime, data.ResponseTime, data.CostPrice, data.ErrorCode, data.ErrorMessage, data.RequestParams, data.ResponseData, data.TransactionId, data.DeleteTime, data.DelState, data.Version, data.Id)
}
return conn.ExecCtx(ctx, query, data.FeatureId, data.ApiId, data.OrderId, data.QueryId, data.CallStatus, data.CallTime, data.ResponseTime, data.CostPrice, data.ErrorCode, data.ErrorMessage, data.RequestParams, data.ResponseData, data.TransactionId, data.DeleteTime, data.DelState, data.Version, data.Id)
}, yccTianyuanapiCallLogIdKey)
}
func (m *defaultTianyuanapiCallLogModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *TianyuanapiCallLog) error {
oldVersion := data.Version
data.Version += 1
var sqlResult sql.Result
var err error
yccTianyuanapiCallLogIdKey := fmt.Sprintf("%s%v", cacheYccTianyuanapiCallLogIdPrefix, data.Id)
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, tianyuanapiCallLogRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.FeatureId, data.ApiId, data.OrderId, data.QueryId, data.CallStatus, data.CallTime, data.ResponseTime, data.CostPrice, data.ErrorCode, data.ErrorMessage, data.RequestParams, data.ResponseData, data.TransactionId, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, data.FeatureId, data.ApiId, data.OrderId, data.QueryId, data.CallStatus, data.CallTime, data.ResponseTime, data.CostPrice, data.ErrorCode, data.ErrorMessage, data.RequestParams, data.ResponseData, data.TransactionId, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion)
}, yccTianyuanapiCallLogIdKey)
if err != nil {
return err
}
updateCount, err := sqlResult.RowsAffected()
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
func (m *defaultTianyuanapiCallLogModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *TianyuanapiCallLog) error {
data.DelState = globalkey.DelStateYes
data.DeleteTime = sql.NullTime{Time: time.Now(), Valid: true}
if err := m.UpdateWithVersion(ctx, session, data); err != nil {
return errors.Wrapf(errors.New("delete soft failed "), "TianyuanapiCallLogModel delete err : %+v", err)
}
return nil
}
func (m *defaultTianyuanapiCallLogModel) FindSum(ctx context.Context, builder squirrel.SelectBuilder, field string) (float64, error) {
if len(field) == 0 {
return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field")
}
builder = builder.Columns("IFNULL(SUM(" + field + "),0)")
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
if err != nil {
return 0, err
}
var resp float64
err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return 0, err
}
}
func (m *defaultTianyuanapiCallLogModel) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64, error) {
if len(field) == 0 {
return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field")
}
builder = builder.Columns("COUNT(" + field + ")")
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
if err != nil {
return 0, err
}
var resp int64
err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return 0, err
}
}
func (m *defaultTianyuanapiCallLogModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*TianyuanapiCallLog, error) {
builder = builder.Columns(tianyuanapiCallLogRows)
if orderBy == "" {
builder = builder.OrderBy("id DESC")
} else {
builder = builder.OrderBy(orderBy)
}
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
if err != nil {
return nil, err
}
var resp []*TianyuanapiCallLog
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultTianyuanapiCallLogModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*TianyuanapiCallLog, error) {
builder = builder.Columns(tianyuanapiCallLogRows)
if orderBy == "" {
builder = builder.OrderBy("id DESC")
} else {
builder = builder.OrderBy(orderBy)
}
if page < 1 {
page = 1
}
offset := (page - 1) * pageSize
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*TianyuanapiCallLog
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultTianyuanapiCallLogModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*TianyuanapiCallLog, int64, error) {
total, err := m.FindCount(ctx, builder, "id")
if err != nil {
return nil, 0, err
}
builder = builder.Columns(tianyuanapiCallLogRows)
if orderBy == "" {
builder = builder.OrderBy("id DESC")
} else {
builder = builder.OrderBy(orderBy)
}
if page < 1 {
page = 1
}
offset := (page - 1) * pageSize
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, total, err
}
var resp []*TianyuanapiCallLog
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, total, nil
default:
return nil, total, err
}
}
func (m *defaultTianyuanapiCallLogModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*TianyuanapiCallLog, error) {
builder = builder.Columns(tianyuanapiCallLogRows)
if preMinId > 0 {
builder = builder.Where(" id < ? ", preMinId)
}
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*TianyuanapiCallLog
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultTianyuanapiCallLogModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*TianyuanapiCallLog, error) {
builder = builder.Columns(tianyuanapiCallLogRows)
if preMaxId > 0 {
builder = builder.Where(" id > ? ", preMaxId)
}
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*TianyuanapiCallLog
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultTianyuanapiCallLogModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error {
return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
return fn(ctx, session)
})
}
func (m *defaultTianyuanapiCallLogModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table)
}
func (m *defaultTianyuanapiCallLogModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
yccTianyuanapiCallLogIdKey := fmt.Sprintf("%s%v", cacheYccTianyuanapiCallLogIdPrefix, id)
_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
if session != nil {
return session.ExecCtx(ctx, query, id)
}
return conn.ExecCtx(ctx, query, id)
}, yccTianyuanapiCallLogIdKey)
return err
}
func (m *defaultTianyuanapiCallLogModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheYccTianyuanapiCallLogIdPrefix, primary)
}
func (m *defaultTianyuanapiCallLogModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", tianyuanapiCallLogRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
}
func (m *defaultTianyuanapiCallLogModel) tableName() string {
return m.table
}

View File

@@ -45,7 +45,7 @@ $tables = @(
# "agent_withdrawal_tax",
# "authorization_document",
# "example",
# "feature"
"feature"
# "alipay_from_callback"
# "global_notifications",
# "order",
@@ -61,10 +61,11 @@ $tables = @(
# "whitelist_order",
# "whitelist_order_item",
# "user_feature_whitelist"
"complaint_main",
"complaint_alipay",
"complaint_alipay_trade",
"complaint_manual"
# "complaint_main",
# "complaint_alipay",
# "complaint_alipay_trade",
# "complaint_manual"
# "tianyuanapi_call_log"
)
# 为每个表生成模型

View File

@@ -0,0 +1,158 @@
# Context参数优化说明
## 优化内容
`ctx context.Context``ProcessRequests` 方法作为参数传入,而不是在每个 `ProcessXXXRequest` 方法内部创建。
## 优化原因
1. **更好的上下文管理**:可以从上层传递 context支持超时、取消等操作
2. **避免重复代码**:不需要在每个方法里都写 `ctx := context.Background()`
3. **更好的可追踪性**:可以通过 context 传递请求ID、用户ID等元数据
4. **符合Go最佳实践**context 应该作为第一个参数传递
## 修改内容
### 1. ProcessRequests 方法签名修改
**修改前:**
```go
func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]byte, error) {
var ctx, cancel = context.WithCancel(context.Background())
defer cancel()
```
**修改后:**
```go
func (a *ApiRequestService) ProcessRequests(ctx context.Context, params []byte, productID string) ([]byte, error) {
var cancel context.CancelFunc
ctx, cancel = context.WithCancel(ctx)
defer cancel()
```
### 2. PreprocessRequestApi 方法修改
**修改前:**
```go
func (a *ApiRequestService) PreprocessRequestApi(params []byte, apiID string) ([]byte, error) {
if processor, exists := requestProcessors[apiID]; exists {
return processor(a, params)
}
return nil, errors.New("api请求, 未找到相应的处理程序")
}
```
**修改后:**
```go
func (a *ApiRequestService) PreprocessRequestApi(ctx context.Context, params []byte, apiID string) ([]byte, error) {
if processor, exists := requestProcessors[apiID]; exists {
return processor(a, ctx, params)
}
return nil, errors.New("api请求, 未找到相应的处理程序")
}
```
### 3. requestProcessors map 类型定义修改
**修改前:**
```go
var requestProcessors = map[string]func(*ApiRequestService, []byte) ([]byte, error){
```
**修改后:**
```go
var requestProcessors = map[string]func(*ApiRequestService, context.Context, []byte) ([]byte, error){
```
### 4. 所有 ProcessXXXRequest 方法签名修改
**修改前:**
```go
func (a *ApiRequestService) ProcessYYSYBE08Request(params []byte) ([]byte, error) {
ctx := context.Background()
// ...
}
```
**修改后:**
```go
func (a *ApiRequestService) ProcessYYSYBE08Request(ctx context.Context, params []byte) ([]byte, error) {
// 移除了 ctx := context.Background()
// ...
}
```
### 5. 错误码提取优化
**修改前:**
```go
if tianyuanErr, ok := err.(*tianyuanapi.Error); ok {
errorCode = fmt.Sprintf("%d", tianyuanErr.Code)
}
```
**修改后:**
```go
if code := tianyuanapi.GetCodeByError(err); code != -1 {
errorCode = fmt.Sprintf("%d", code)
}
```
## 修改的方法列表32个
所有以下方法都已添加 `ctx context.Context` 作为第一个参数,并移除了内部的 `ctx := context.Background()` 声明:
1. ProcessPersonEnterpriseProRequest
2. ProcessBehaviorRiskScanRequest
3. ProcessYYSYBE08Request
4. ProcessYYSY09CDRequest
5. ProcessFLXG0687Request
6. ProcessFLXG3D56Request
7. ProcessFLXG0V4BRequest
8. ProcessQYGL8271Request
9. ProcessIVYZ5733Request
10. ProcessIVYZ9A2BRequest
11. ProcessJRZQ0A03Request
12. ProcessQYGL6F2DRequest
13. ProcessJRZQ8203Request
14. ProcessJRZQ4AA8Request
15. ProcessQCXG7A2BRequest
16. ProcessDWBG8B4DRequest
17. ProcessDWBG6A2CRequest
18. ProcessJRZQ4B6CRequest
19. ProcessJRZQ09J8Request
20. ProcessJRZQ5E9FRequest
21. ProcessQYGL3F8ERequest
22. ProcessIVYZ81NCRequest
23. ProcessIVYZ7F3ARequest
24. ProcessDWBG7F3ARequest
25. ProcessJRZQ8A2DRequest
26. ProcessYYSY8B1CRequest
27. ProcessYYSY7D3ERequest
28. ProcessFLXG7E8FRequest
29. ProcessIVYZ8I9JRequest
30. ProcessJRZQ7F1ARequest
31. ProcessIVYZ3P9MRequest
32. ProcessJRZQ6F2ARequest
## 调用方验证
调用方 `paySuccessNotify.go` 已经正确传递了 `ctx`
```go
combinedResponse, err := l.svcCtx.ApiRequestService.ProcessRequests(ctx, decryptData, product.Id)
```
## 优势
1. **统一的上下文管理**所有API调用共享同一个 context可以统一控制超时和取消
2. **更好的错误追踪**:可以在 context 中传递请求ID、用户ID等信息方便日志追踪
3. **代码更简洁**:不需要在每个方法中重复创建 context
4. **符合Go规范**context 作为第一个参数是 Go 的标准做法
## 注意事项
- 所有方法现在都依赖外部传入的 `ctx`,不再内部创建
- `ProcessRequests` 方法内部仍然会创建一个带 cancel 的 context用于控制并发请求的取消
- 如果将来需要在 context 中传递元数据如请求ID、用户ID可以在调用 `ProcessRequests` 之前设置

View File

@@ -0,0 +1,35 @@
#!/bin/bash
# 天元API调用记录表 - Model生成脚本
# 数据库配置从main.yaml中提取
DB_HOST="ycc_mysql:3306"
DB_USER="ycc"
DB_PASS="5vg67b3UNHu8"
DB_NAME="ycc"
MODEL_DIR="../../app/main/model"
TEMPLATE_DIR="./template"
# 生成feature表已添加cost_price字段
echo "正在生成 feature 表Model..."
goctl model mysql datasource -url="$DB_USER:$DB_PASS@tcp($DB_HOST)/$DB_NAME" -table="feature" -dir="$MODEL_DIR" -cache=true --style=goZero --home="$TEMPLATE_DIR"
# 生成tianyuanapi_call_log表
echo "正在生成 tianyuanapi_call_log 表Model..."
goctl model mysql datasource -url="$DB_USER:$DB_PASS@tcp($DB_HOST)/$DB_NAME" -table="tianyuanapi_call_log" -dir="$MODEL_DIR" -cache=true --style=goZero --home="$TEMPLATE_DIR"
echo "========================================="
echo "Model生成完成"
echo "========================================="
echo "生成的文件:"
echo " 1. featureModel_gen.go (已更新)"
echo " 2. featureModel.go (已更新)"
echo " 3. tianyuanapiCallLogModel_gen.go (新建)"
echo " 4. tianyuanapiCallLogModel.go (新建)"
echo "========================================="
echo "下一步:"
echo " 1. 检查生成的Model文件"
echo " 2. 更新svc/servicecontext.go添加新Model的初始化"
echo " 3. 在apirequestService.go中添加调用记录逻辑"
echo "========================================="

View File

@@ -0,0 +1,64 @@
-- ============================================
-- Feature表 - 添加成本价字段
-- 说明:为 feature 表添加天远API调用成本价字段
-- 执行时间2025-01-13
-- ============================================
-- 添加成本价字段
ALTER TABLE `feature`
ADD COLUMN `cost_price` decimal(10, 2) DEFAULT 0.00 COMMENT '天远API调用成本价单位' AFTER `whitelist_price`;
-- ============================================
-- 说明:
-- 1. cost_price 默认值为 0.00表示该feature的成本价为0元
-- 2. 后台可以在"功能管理"页面配置每个feature的调用成本
-- 3. 成本价用于统计天远API的调用成本
-- ============================================
-- ============================================
-- 天远API调用记录表
-- 说明记录每次调用天远API的详细信息
-- ============================================
CREATE TABLE `tianyuanapi_call_log` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`feature_id` varchar(36) NOT NULL COMMENT '功能ID关联feature表',
`api_id` varchar(50) NOT NULL COMMENT 'API标识YYSYBE08',
`order_id` varchar(36) DEFAULT NULL COMMENT '订单ID关联order表',
`query_id` varchar(36) DEFAULT NULL COMMENT '查询ID关联query表',
`call_status` tinyint NOT NULL DEFAULT 0 COMMENT '调用状态0=失败1=成功',
`call_time` datetime NOT NULL COMMENT '调用时间',
`response_time` int DEFAULT NULL COMMENT '响应耗时(毫秒)',
`cost_price` decimal(10, 2) DEFAULT 0.00 COMMENT '本次调用成本成功时从feature.cost_price获取失败时为0',
`error_code` varchar(50) DEFAULT NULL COMMENT '错误码(失败时记录)',
`error_message` varchar(500) DEFAULT NULL COMMENT '错误信息(失败时记录)',
`request_params` text DEFAULT NULL COMMENT '请求参数JSON格式',
`response_data` text DEFAULT NULL COMMENT '响应数据JSON格式仅记录关键信息',
`transaction_id` varchar(100) DEFAULT NULL COMMENT '天远API流水号',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态0=未删除1=已删除',
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
PRIMARY KEY (`id`),
KEY `idx_feature_id` (`feature_id`),
KEY `idx_api_id` (`api_id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_query_id` (`query_id`),
KEY `idx_call_status` (`call_status`),
KEY `idx_call_time` (`call_time`),
KEY `idx_feature_time` (`feature_id`, `call_time`) COMMENT '复合索引:查询某个功能在某段时间的调用记录'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '天远API调用记录表';
-- ============================================
-- 说明:
-- 1. feature_id: 关联到feature表记录是哪个接口的调用
-- 2. api_id: 天远API的接口标识YYSYBE08
-- 3. order_id/query_id: 关联订单或查询,方便追溯
-- 4. call_status: 调用状态,成功=1失败=0
-- 5. cost_price: 成本价成功时为feature.cost_price失败时为0
-- 6. error_code/error_message: 失败时记录错误信息
-- 7. request_params/response_data: 请求和响应数据(可选择性记录)
-- 8. transaction_id: 天远API返回的流水号
-- 9. 无论成功失败都会记录,方便统计分析
-- ============================================

View File

@@ -0,0 +1,183 @@
# 优化后的批量修改说明 - 使用ctx参数
## 优化说明
根据建议,我们通过 `ProcessRequests` 方法传入 `ctx context.Context` 参数,而不是在每个方法内部创建 `context.Background()`
这样的好处:
1. **符合Go最佳实践**Context应该从上层传入
2. **更好的控制**:可以统一控制请求超时、取消等
3. **代码更简洁**不需要在每个方法中重复创建context
4. **更好的追踪**:可以统一传递追踪信息
## 已完成的修改
### 1. 修改了 ProcessRequests 方法签名
```go
func (a *ApiRequestService) ProcessRequests(ctx context.Context, params []byte, productID string) ([]byte, error)
```
- 移除了内部的 `ctx, cancel = context.WithCancel(context.Background())`
- 直接使用外部传入的 `ctx`
### 2. 定义了 RequestProcessor 类型
```go
type RequestProcessor func(ctx context.Context, a *ApiRequestService, params []byte) ([]byte, error)
```
### 3. 更新了 requestProcessors 映射
所有方法签名的类型都更新为接收 `context.Context` 参数
### 4. 修改了 PreprocessRequestApi 方法
```go
func (a *ApiRequestService) PreprocessRequestApi(ctx context.Context, params []byte, apiID string) ([]byte, error) {
if processor, exists := requestProcessors[apiID]; exists {
return processor(ctx, a, params) // 传递ctx
}
return nil, errors.New("api请求, 未找到相应的处理程序")
}
```
### 5. 修改了示例方法
- `ProcessYYSYBE08Request` ✅ - 已添加 `ctx context.Context` 参数
- `ProcessFLXG0V4BRequest` ✅ - 已添加 `ctx context.Context` 参数
## 需要完成的修改
现在还需要修改剩余30个方法的签名在每个方法签名的 `params` 参数前添加 `ctx context.Context` 参数。
### 方法1使用批量修改脚本推荐
#### Linux/Mac 环境
```bash
cd ycc-proxy-server
chmod +x deploy/sql/批量修改方法签名.sh
./deploy/sql/批量修改方法签名.sh
```
这个脚本会:
1. 备份 `apirequestService.go` 文件
2. 批量修改31个方法的签名
3. 显示修改统计
### 方法2使用IDE查找替换Windows环境推荐
#### 步骤1在IDE中打开查找替换
打开 `apirequestService.go`,按 `Ctrl+H` 打开查找替换
#### 步骤2查找并替换
**查找内容:**
```text
func (a *ApiRequestService) Process(params []byte) ([]byte, error)
```
**替换为:**
```text
func (a *ApiRequestService) Process(ctx context.Context, params []byte) ([]byte, error)
```
**注意:**
- 这个模式会匹配所有 `Process*Request` 方法
- 点击"全部替换"即可
#### 步骤3验证替换
替换后检查是否所有31个方法都成功修改。如果没有修改成功可能是因为
1. 某些方法已经修改过了
2. 方法签名格式有差异
### 方法3逐个手动修改
如果批量方法不适用,可以逐个修改。需要修改的方法列表:
#### 已修改 ✅
1. ProcessYYSYBE08Request
2. ProcessFLXG0V4BRequest
#### 待修改 ⏳
1. ProcessPersonEnterpriseProRequest (第312行)
2. ProcessFLXG0687Request (第671行)
3. ProcessFLXG3D56Request (第699行)
4. ProcessIVYZ5733Request (第754行)
5. ProcessIVYZ9A2BRequest (第810行)
6. ProcessJRZQ0A03Request (第922行)
7. ProcessJRZQ8203Request (第979行)
8. ProcessJRZQ4AA8Request (第1036行)
9. ProcessQYGL8271Request (第1085行)
10. ProcessQYGL6F2DRequest (第1150行)
11. ProcessQCXG7A2BRequest (第1191行)
12. ProcessYYSY09CDRequest (第1211行)
13. ProcessBehaviorRiskScanRequest (第1247行)
14. ProcessDWBG8B4DRequest (第1314行)
15. ProcessDWBG6A2CRequest (第1339行)
16. ProcessJRZQ4B6CRequest (第1363行)
17. ProcessJRZQ09J8Request (第1387行)
18. ProcessJRZQ5E9FRequest (第1411行)
19. ProcessQYGL3F8ERequest (第1433行)
20. ProcessIVYZ81NCRequest (第1461行)
21. ProcessIVYZ7F3ARequest (第1482行)
22. ProcessDWBG7F3ARequest (第1505行)
23. ProcessJRZQ8A2DRequest (第1528行)
24. ProcessYYSY8B1CRequest (第1550行)
25. ProcessYYSY7D3ERequest (第1569行)
26. ProcessFLXG7E8FRequest (第1590行)
27. ProcessIVYZ8I9JRequest (第1613行)
28. ProcessJRZQ7F1ARequest (第1636行)
29. ProcessIVYZ3P9MRequest (第1659行)
30. ProcessJRZQ6F2ARequest (第1680行)
每个方法需要做的修改:
**修改前:**
```go
func (a *ApiRequestService) ProcessMethodName(params []byte) ([]byte, error) {
// 方法体
}
```
**修改后:**
```go
func (a *ApiRequestService) ProcessMethodName(ctx context.Context, params []byte) ([]byte, error) {
// 方法体
}
```
## 注意事项
1. **不需要在每个方法中添加 `ctx := context.Background()`**
- 因为现在通过参数传入
- 使用 `ctx context.Context` 参数即可
2. **`callTianyuanApiWithLog` 方法已经支持传入 ctx**
- 如果第一个参数为空字符串 `""`会自动从缓存获取featureID
- 否则使用传入的featureID
3. **不需要修改方法内部的 API 调用**
- 因为我们已经修改了部分方法使用 `callTianyuanApiWithLog`
- 批量替换后,记得将所有 `a.tianyuanapi.CallInterface` 替换为 `a.callTianyuanApiWithLog(ctx, "",`
## 完成后验证
1. **编译项目**
```bash
cd ycc-proxy-server
go build ./...
```
2. **检查编译错误**
- 主要是方法签名不匹配的错误
- 如果有错误,检查哪些方法没有正确修改
3. **运行测试**
- 发起几个API请求
- 检查 `tianyuanapi_call_log`
- 验证日志是否正确记录
## 优势总结
使用 `ctx` 参数的方式:
**代码更简洁** - 不需要重复创建context
**更好的控制** - 可以统一管理超时、取消
**符合最佳实践** - Context应该从上层传入
**便于追踪** - 可以在ctx中传递追踪信息
**便于测试** - 测试时可以传入自定义的ctx

View File

@@ -0,0 +1,135 @@
# 利润统计API成本计入说明
## 修改内容
在后台统计面板的利润统计部分将API调用成本计入成本计算中。
## 修改位置
文件:`ycc-proxy-server/app/main/api/internal/logic/admin_dashboard/admingetdashboardstatisticslogic.go`
方法:`calculateProfitStatistics`
## 修改详情
### 1. 今日利润计算
**修改前:**
```go
// 今日利润 = 营收 - 佣金 - 返利 - 税务成本 + 提现收税
stats.TodayProfit = todayRevenue - todayCommission - todayRebate - todayCompanyTax + todayTaxIncome
```
**修改后:**
```go
// 今日API调用成本
todayApiCost := 0.0
if l.svcCtx.TianyuanapiCallLogService != nil {
todayApiStats, err := l.svcCtx.TianyuanapiCallLogService.GetStatistics(l.ctx, service.StatisticsFilter{
StartDate: todayStart,
EndDate: todayEnd,
})
if err != nil {
logx.Errorf("获取今日API调用成本失败: %v", err)
} else {
todayApiCost = todayApiStats.TotalCost
}
}
// 今日利润 = 营收 - 佣金 - 返利 - 税务成本 - API调用成本 + 提现收税
stats.TodayProfit = todayRevenue - todayCommission - todayRebate - todayCompanyTax - todayApiCost + todayTaxIncome
```
### 2. 当月利润计算
**修改前:**
```go
// 当月利润
stats.MonthProfit = monthRevenue - monthCommission - monthRebate - monthCompanyTax + monthTaxIncome
```
**修改后:**
```go
// 当月API调用成本
monthApiCost := 0.0
if l.svcCtx.TianyuanapiCallLogService != nil {
monthApiStats, err := l.svcCtx.TianyuanapiCallLogService.GetStatistics(l.ctx, service.StatisticsFilter{
StartDate: monthStart,
EndDate: monthEnd,
})
if err != nil {
logx.Errorf("获取当月API调用成本失败: %v", err)
} else {
monthApiCost = monthApiStats.TotalCost
}
}
// 当月利润
stats.MonthProfit = monthRevenue - monthCommission - monthRebate - monthCompanyTax - monthApiCost + monthTaxIncome
```
### 3. 总利润计算
**修改前:**
```go
// 总利润
stats.TotalProfit = totalRevenue - totalCommission - totalRebate - totalCompanyTax + totalTaxIncome
```
**修改后:**
```go
// 总API调用成本
totalApiCost := 0.0
if l.svcCtx.TianyuanapiCallLogService != nil {
totalApiStats, err := l.svcCtx.TianyuanapiCallLogService.GetStatistics(l.ctx, service.StatisticsFilter{})
if err != nil {
logx.Errorf("获取总API调用成本失败: %v", err)
} else {
totalApiCost = totalApiStats.TotalCost
}
}
// 总利润
stats.TotalProfit = totalRevenue - totalCommission - totalRebate - totalCompanyTax - totalApiCost + totalTaxIncome
```
## 利润计算公式
修改后的利润计算公式为:
```
利润 = 营收 - 佣金 - 返利 - 税务成本 - API调用成本 + 提现收税
```
其中:
- **营收**订单金额总和status = 'paid'
- **佣金**代理佣金总和status != 3
- **返利**代理返利总和status != 3
- **税务成本**:订单金额的 6%
- **API调用成本**天元API成功调用的成本总和`tianyuanapi_call_log` 表统计)
- **提现收税**代理提现税总和tax_status = 2
## API调用成本统计说明
API调用成本通过 `TianyuanapiCallLogService.GetStatistics` 方法获取:
1. **今日API调用成本**:统计 `call_time >= todayStart AND call_time < todayEnd` 的成功调用成本
2. **当月API调用成本**:统计 `call_time >= monthStart AND call_time < monthEnd` 的成功调用成本
3. **总API调用成本**:统计所有成功调用的成本
**注意**:只有成功调用(`call_status = 1`)才会计入成本,失败调用不计成本。
## 错误处理
如果获取API调用成本失败会记录错误日志但不会影响利润计算API成本默认为0确保统计功能的稳定性。
## 依赖项
- `TianyuanapiCallLogService`:需要在 `servicecontext.go` 中初始化
- `tianyuanapi_call_log` 表:需要已创建并包含调用记录数据
## 验证
修改完成后,可以通过以下方式验证:
1. 查看后台统计面板的利润统计数据
2. 检查日志确认API调用成本是否正确获取
3. 对比修改前后的利润数据确认API成本已正确计入

View File

@@ -0,0 +1,149 @@
# 利润统计面板改造说明
## 改造内容
将利润统计从原来的卡片布局改为右侧栏面板,并展示详细的成本和收入明细。
## 修改文件
### 1. 后端API定义
**文件:** `ycc-proxy-server/app/main/api/desc/admin/dashboard.api`
**修改内容:**
-`AdminProfitStatistics` 中添加了三个明细字段:
- `TodayDetail AdminProfitDetail` - 今日明细
- `MonthDetail AdminProfitDetail` - 当月明细
- `TotalDetail AdminProfitDetail` - 总计明细
- 新增 `AdminProfitDetail` 类型,包含:
- `Revenue` - 营收
- `Commission` - 佣金
- `Rebate` - 返利
- `CompanyTax` - 税务成本
- `ApiCost` - API调用成本
- `TaxIncome` - 提现收税
- `Profit` - 利润
- `ProfitRate` - 利润率
### 2. 后端逻辑
**文件:** `ycc-proxy-server/app/main/api/internal/logic/admin_dashboard/admingetdashboardstatisticslogic.go`
**修改内容:**
-`calculateProfitStatistics` 方法中,为今日、当月、总计三个时间范围都填充了明细数据
- 明细数据包含所有成本和收入的详细金额
### 3. 前端类型定义
**文件:** `ycc-proxy-admin/apps/web-antd/src/api/dashboard/dashboard.ts`
**修改内容:**
- 添加了 `ProfitDetail` 接口
- 更新了 `ProfitStatistics` 接口,添加三个明细字段
### 4. 前端主页面
**文件:** `ycc-proxy-admin/apps/web-antd/src/views/dashboard/analytics/index.vue`
**修改内容:**
- 移除了利润统计卡片从4列布局改为3列布局
- 改为左右布局:左侧是统计卡片和图表,右侧是利润统计面板
- 添加了响应式布局,小屏幕下右侧面板会移到下方
### 5. 前端利润面板组件(新建)
**文件:** `ycc-proxy-admin/apps/web-antd/src/views/dashboard/analytics/analytics-profit-panel.vue`
**功能:**
- 展示利润总额和利润率
- 通过Tab切换今日/当月/总计
- 详细展示收入明细(营收、提现收税)
- 详细展示成本明细代理佣金、代理返利、税务成本、API调用成本
- 分别计算收入合计和成本合计
## 布局说明
### 桌面端(>1200px
```
┌─────────────────────────────────┬──────────────┐
│ │ │
│ 统计卡片3列 │ 利润统计 │
│ │ 面板 │
│ 趋势图表 │ (固定) │
│ │ │
└─────────────────────────────────┴──────────────┘
```
### 移动端≤1200px
```
┌─────────────────────────────────┐
│ 统计卡片3列
│ │
│ 趋势图表 │
│ │
│ 利润统计面板 │
│ (移到下方) │
└─────────────────────────────────┘
```
## 明细数据说明
### 收入项
1. **营收**订单金额总和status = 'paid'
2. **提现收税**代理提现税总和tax_status = 2
### 成本项
1. **代理佣金**代理佣金总和status != 3
2. **代理返利**代理返利总和status != 3
3. **税务成本**:订单金额的 6%
4. **API调用成本**天元API成功调用的成本总和
### 利润计算
```
利润 = 营收 + 提现收税 - 代理佣金 - 代理返利 - 税务成本 - API调用成本
利润率 = (利润 / 营收) × 100%
```
## 使用步骤
### 1. 生成API类型后端
运行API生成脚本
```bash
cd ycc-proxy-server
# 运行gen_api脚本生成types
```
### 2. 编译验证
```bash
# 后端编译
cd ycc-proxy-server
go build
# 前端编译
cd ycc-proxy-admin
npm run build
```
### 3. 测试验证
1. 打开后台统计面板
2. 查看右侧利润统计面板
3. 切换今日/当月/总计Tab
4. 验证各项成本和收入明细是否正确显示
5. 验证收入合计和成本合计计算是否正确
## 注意事项
1. **必须运行gen_api脚本**修改了API定义后需要运行gen_api脚本生成新的types.go文件
2. **数据准确性**确保API调用成本数据已正确记录在 `tianyuanapi_call_log` 表中
3. **响应式布局**:在小屏幕设备上,右侧面板会自动移到下方,确保良好的用户体验
## UI特性
- **固定定位**右侧面板在桌面端使用sticky定位滚动时保持可见
- **颜色区分**:收入用绿色(+),成本用红色(-),利润根据正负显示不同颜色
- **Tab切换**:可以快速切换查看不同时间范围的明细
- **合计显示**:分别显示收入合计和成本合计,方便查看

View File

@@ -0,0 +1,159 @@
# 天元API调用记录功能 - 代码修改说明
## 概述
本文档说明如何修改 `apirequestService.go`为每次天元API调用添加记录功能。
## 已完成的修改
### 1. 数据库层面
- ✅ 创建 `tianyuanapi_cost_migration.sql` 脚本
-`feature` 表添加 `cost_price` 字段
- 创建 `tianyuanapi_call_log`
### 2. Service层面
- ✅ 创建 `tianyuanapiCallLogService.go` 服务
- `RecordCall()` - 记录API调用
- `GetStatistics()` - 获取统计信息
### 3. Model层面
- ⏳ 需要执行 `generate_tianyuanapi_models.sh` 生成Model代码
### 4. ApiRequestService修改
- ✅ 添加 `tianyuanapiCallLogService` 字段
- ✅ 修改构造函数,添加 `tianyuanapiCallLogService` 参数
- ✅ 创建 `callTianyuanApiWithLog()` 辅助方法
## 待完成的修改
### 1. 执行SQL脚本
```bash
# 在数据库中执行迁移脚本
mysql -u root -p ycc < ycc-proxy-server/deploy/sql/tianyuanapi_cost_migration.sql
```
### 2. 生成Model代码
```bash
cd ycc-proxy-server/deploy/sql
bash generate_tianyuanapi_models.sh
```
### 3. 更新 servicecontext.go
`svc/servicecontext.go` 中添加:
```go
// 初始化Model
tianyuanapiCallLogModel := model.NewTianyuanapiCallLogModel(db, cacheConf)
// 初始化Service
tianyuanapiCallLogService := service.NewTianyuanapiCallLogService(
tianyuanapiCallLogModel,
featureModel,
)
// 修改ApiRequestService初始化
apiRequestService := service.NewApiRequestService(
c,
featureModel,
productFeatureModel,
userFeatureWhitelistModel,
tianyuanapi,
tianyuanapiCallLogService, // 新增参数
)
// 在ServiceContext结构体中添加字段
TianyuanapiCallLogModel model.TianyuanapiCallLogModel
TianyuanapiCallLogService *service.TianyuanapiCallLogService
// 在返回的ServiceContext中添加
TianyuanapiCallLogModel: tianyuanapiCallLogModel,
TianyuanapiCallLogService: tianyuanapiCallLogService,
```
### 4. 修改API调用方法示例
#### 原代码:
```go
resp, err := a.callTianyuanApiWithLog(ctx, "", "QYGLB4C0", map[string]interface{}{
"id_card": idCard.String(),
})
```
#### 修改后:
```go
resp, err := a.callTianyuanApiWithLog(ctx, featureID, "QYGLB4C0", map[string]interface{}{
"id_card": idCard.String(),
})
```
### 需要修改的方法列表共32个
1. `ProcessPersonEnterpriseProRequest` - 需要修改2处
- 第253行`QYGLB4C0` - 主接口
- 第455行`QYGL8271` - 涉诉信息查询
2. `ProcessFLXG0V4BRequest` - 第586行`FLXG0V4B`
3. `ProcessFLXG0687Request` - 第608行`FLXG0687`
4. `ProcessFLXG3D56Request` - 第638行`FLXG3D56`
5. `ProcessIVYZ5733Request` - 第692行`IVYZ5733`
6. `ProcessIVYZ9A2BRequest` - 第748行`IVYZ9A2B`
7. `ProcessYYSYBE08Request` - 第819行`YYSYBE08`
8. `ProcessJRZQ0A03Request` - 第861行`JRZQ0A03`
9. `ProcessJRZQ8203Request` - 第918行`JRZQ8203`
10. `ProcessJRZQ4AA8Request` - 第975行`JRZQ4AA8`
11. `ProcessQYGL8271Request` - 第1024行`QYGL8271`
12. `ProcessQYGL6F2DRequest` - 第1082行`QYGL6F2D`
13. `ProcessQCXG7A2BRequest` - 第1123行`QCXG7A2B`
14. `ProcessYYSY09CDRequest` - 第1143行`YYSY09CD`
15. `ProcessDWBG8B4DRequest` - 第1254行`DWBG8B4D`
16. `ProcessDWBG6A2CRequest` - 第1279行`DWBG6A2C`
17. `ProcessJRZQ4B6CRequest` - 第1303行`JRZQ4B6C`
18. `ProcessJRZQ09J8Request` - 第1327行`JRZQ09J8`
19. `ProcessJRZQ5E9FRequest` - 第1351行`JRZQ5E9F`
20. `ProcessQYGL3F8ERequest` - 第1373行`QYGL3F8E`
21. `ProcessIVYZ81NCRequest` - 第1393行`IVYZ81NC`
22. `ProcessIVYZ7F3ARequest` - 第1414行`IVYZ7F3A`
23. `ProcessDWBG7F3ARequest` - 第1437行`DWBG7F3A`
24. `ProcessJRZQ8A2DRequest` - 第1460行`JRZQ8A2D`
25. `ProcessYYSY8B1CRequest` - 第1482行`YYSY8B1C`
26. `ProcessYYSY7D3ERequest` - 第1501行`YYSY7D3E`
27. `ProcessFLXG7E8FRequest` - 第1522行`FLXG7E8F`
28. `ProcessIVYZ8I9JRequest` - 第1545行`IVYZ8I9J`
29. `ProcessJRZQ7F1ARequest` - 第1568行`JRZQ7F1A`
30. `ProcessIVYZ3P9MRequest` - 第1591行`IVYZ3P9M`
31. `ProcessJRZQ6F2ARequest` - 第1612行`JRZQ6F2A`
### 注意事项
1. **featureID获取**:每个方法中已经有了 `feature` 对象,直接使用 `feature.Id` 即可
2. **context传递**:需要从方法签名中获取 `ctx context.Context` 参数
3. **错误处理**`callTianyuanApiWithLog` 已经包含了错误处理,无需额外处理
4. **批量修改建议**:可以使用查找替换功能批量修改
- 查找:`a.callTianyuanApiWithLog(ctx, "", "`
- 替换为:`a.callTianyuanApiWithLog(ctx, feature.Id, "`
## 统计功能使用示例
```go
// 获取某个功能在某个时间段的调用统计
filter := StatisticsFilter{
FeatureID: "feature-id-123",
StartDate: time.Now().AddDate(0, 0, -30), // 30天前
EndDate: time.Now(),
}
stats, err := ctx.TianyuanapiCallLogService.GetStatistics(context.Background(), filter)
if err == nil {
fmt.Printf("总调用次数: %d\n", stats.TotalCalls)
fmt.Printf("成功次数: %d\n", stats.SuccessCalls)
fmt.Printf("失败次数: %d\n", stats.FailedCalls)
fmt.Printf("总成本: %.2f元\n", stats.TotalCost)
}
```
## 验证步骤
1. 执行SQL脚本检查表是否创建成功
2. 运行Model生成脚本检查Model文件是否生成
3. 编译项目,检查是否有编译错误
4. 运行项目发起一个API请求
5. 检查 `tianyuanapi_call_log` 表,验证记录是否正确插入
6. 检查成本价是否正确计算成功调用有成本失败调用成本为0

View File

@@ -0,0 +1,297 @@
# 天元API调用记录功能 - 实施总结
## 功能概述
本功能实现了天元API调用的成本记录包括
1.`feature` 表添加 `cost_price` 字段,存储每个接口的成本价
2. 创建 `tianyuanapi_call_log`记录每次API调用的详细信息
3. 无论成功失败都记录,但只有成功调用才计算成本
4. 提供统计功能,可查询时间段内的调用次数和总成本
## 已完成的工作
### 1. 数据库层面 ✅
**文件:** `ycc-proxy-server/deploy/sql/tianyuanapi_cost_migration.sql`
**修改内容:**
-`feature` 表添加 `cost_price` 字段decimal(10, 2)默认0.00
- 创建 `tianyuanapi_call_log` 表,包含以下字段:
- `feature_id`: 关联feature表
- `api_id`: 天元API标识YYSYBE08
- `order_id`: 关联订单(可选)
- `query_id`: 关联查询(可选)
- `call_status`: 调用状态0=失败1=成功)
- `call_time`: 调用时间
- `response_time`: 响应耗时(毫秒)
- `cost_price`: 本次调用成本成功时有值失败为0
- `error_code`: 错误码(失败时)
- `error_message`: 错误信息(失败时)
- `request_params`: 请求参数JSON
- `response_data`: 响应数据JSON截取前1000字符
- `transaction_id`: 天元API流水号
**索引设计:**
- `idx_feature_id`: 按feature_id查询
- `idx_api_id`: 按api_id查询
- `idx_order_id`: 按order_id查询
- `idx_query_id`: 按query_id查询
- `idx_call_status`: 按调用状态查询
- `idx_call_time`: 按调用时间查询
- `idx_feature_time`: 复合索引feature_id + call_time
### 2. Service层面 ✅
**文件:** `ycc-proxy-server/app/main/api/internal/service/tianyuanapiCallLogService.go`
**新增方法:**
1. `RecordCall(ctx context.Context, opts CallLogOptions) error`
- 记录API调用
- 自动获取feature的成本价
- 成功时记录成本失败时成本为0
- 异步记录,不影响主流程
2. `GetStatistics(ctx context.Context, filter StatisticsFilter) (*Statistics, error)`
- 获取统计信息
- 支持按feature_id、api_id、时间范围过滤
- 返回:总调用次数、成功次数、失败次数、总成本
### 3. ApiRequestService修改 ✅
**文件:** `ycc-proxy-server/app/main/api/internal/service/apirequestService.go`
**新增字段:**
- `tianyuanapiCallLogService *TianyuanapiCallLogService`
- `apiFeatureMapCache map[string]string` - apiID到featureID的缓存
- `apiFeatureMapMutex sync.RWMutex` - 缓存读写锁
**新增方法:**
1. `callTianyuanApiWithLog(ctx context.Context, featureID, apiID string, params map[string]interface{}) (*tianyuanapi.Response, error)`
- 调用天元API
- 记录调用开始时间
- 计算响应耗时
- 异步记录调用日志
- 返回响应和错误
2.`ProcessRequests` 方法中构建 `apiID -> featureID` 映射缓存
**修改的方法(示例):**
- `ProcessYYSYBE08Request` - 已修改
- `ProcessFLXG0V4BRequest` - 已修改
**待修改的方法30个**
剩余30个方法需要将 `a.callTianyuanApiWithLog(ctx, "", "API名称", ...)`
替换为 `a.callTianyuanApiWithLog(ctx, "", "API名称", ...)`
详细列表见:`批量替换API调用说明.md`
### 4. ServiceContext修改 ✅
**文件:** `ycc-proxy-server/app/main/api/internal/svc/servicecontext.go`
**新增模型:**
```go
TianyuanapiCallLogModel model.TianyuanapiCallLogModel
TianyuanapiCallLogService *service.TianyuanapiCallLogService
```
**新增初始化:**
```go
tianyuanapiCallLogModel := model.NewTianyuanapiCallLogModel(db, cacheConf)
tianyuanapiCallLogService := service.NewTianyuanapiCallLogService(tianyuanapiCallLogModel, featureModel)
```
**修改ApiRequestService初始化**
```go
apiRequestService := service.NewApiRequestService(
c,
featureModel,
productFeatureModel,
userFeatureWhitelistModel,
tianyuanapi,
tianyuanapiCallLogService, // 新增参数
)
```
### 5. Model生成脚本 ✅
**文件:** `ycc-proxy-server/deploy/sql/generate_tianyuanapi_models.sh`
**功能:**
- 生成 `feature` 表的Model已更新包含cost_price字段
- 生成 `tianyuanapi_call_log` 表的Model
### 6. 文档 ✅
**已创建的文档:**
1. `天元API调用记录修改说明.md` - 详细的修改步骤和示例
2. `批量替换API调用说明.md` - 批量修改API调用方法的指南
## 待完成的工作
### 1. 执行SQL脚本 ⏳
```bash
# 在数据库中执行迁移脚本
mysql -u root -p ycc < ycc-proxy-server/deploy/sql/tianyuanapi_cost_migration.sql
```
### 2. 生成Model代码 ⏳
```bash
cd ycc-proxy-server/deploy/sql
bash generate_tianyuanapi_models.sh
```
这将生成以下文件:
- `app/main/model/featureModel_gen.go` 已更新包含cost_price字段
- `app/main/model/featureModel.go` (已更新)
- `app/main/model/tianyuanapiCallLogModel_gen.go` (新建)
- `app/main/model/tianyuanapiCallLogModel.go` (新建)
### 3. 批量修改API调用方法 ⏳
**需要修改30个方法**
```go
resp, err := a.callTianyuanApiWithLog(ctx, "", "API名称", map[string]interface{}{
```
**替换为:**
```go
ctx := context.Background()
resp, err := a.callTianyuanApiWithLog(ctx, "", "API名称", map[string]interface{}{
```
**参考文档:** `批量替换API调用说明.md`
**建议使用IDE批量替换功能**
1. 打开 `apirequestService.go`
2.`Ctrl+H` 打开查找替换
3. 查找:`a.callTianyuanApiWithLog(ctx, "", "`
4. 替换:`a.callTianyuanApiWithLog(ctx, "", "`
5. 全部替换
**然后手动在每个方法开头添加:**
```go
ctx := context.Background()
```
### 4. 编译验证 ⏳
```bash
cd ycc-proxy-server
go build ./...
```
检查是否有编译错误。
### 5. 测试验证 ⏳
1. 运行项目
2. 发起几个API请求成功和失败各几次
3. 检查 `tianyuanapi_call_log` 表,验证:
- 记录是否正确插入
- 成功调用有正确的成本价
- 失败调用成本为0
- 响应耗时是否记录
- 错误信息是否正确
### 6. 后台配置 ⏳
在后台"功能管理"页面为每个feature配置成本价
1. 进入"功能管理"
2. 编辑某个功能
3. 设置"天元API调用成本价"字段
4. 保存
## 统计功能使用示例
```go
// 获取某个功能在某个时间段的调用统计
filter := StatisticsFilter{
FeatureID: "feature-id-123",
StartDate: time.Now().AddDate(0, 0, -30), // 30天前
EndDate: time.Now(),
}
stats, err := ctx.TianyuanapiCallLogService.GetStatistics(context.Background(), filter)
if err == nil {
fmt.Printf("总调用次数: %d\n", stats.TotalCalls)
fmt.Printf("成功次数: %d\n", stats.SuccessCalls)
fmt.Printf("失败次数: %d\n", stats.FailedCalls)
fmt.Printf("总成本: %.2f元\n", stats.TotalCost)
}
```
## SQL查询示例
### 查询某个接口的调用记录
```sql
SELECT * FROM tianyuanapi_call_log
WHERE api_id = 'YYSYBE08'
ORDER BY call_time DESC
LIMIT 100;
```
### 统计某天所有接口的调用情况
```sql
SELECT
api_id,
COUNT(*) as total_calls,
SUM(CASE WHEN call_status = 1 THEN 1 ELSE 0 END) as success_calls,
SUM(CASE WHEN call_status = 0 THEN 1 ELSE 0 END) as failed_calls,
SUM(cost_price) as total_cost
FROM tianyuanapi_call_log
WHERE DATE(call_time) = CURDATE()
GROUP BY api_id;
```
### 查询成功率最低的接口
```sql
SELECT
api_id,
COUNT(*) as total_calls,
SUM(CASE WHEN call_status = 1 THEN 1 ELSE 0 END) as success_calls,
(SUM(CASE WHEN call_status = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*)) as success_rate
FROM tianyuanapi_call_log
WHERE call_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY api_id
ORDER BY success_rate ASC
LIMIT 10;
```
## 注意事项
1. **成本价配置**需要在后台为每个feature配置成本价否则所有调用的成本都是0
2. **性能考虑**调用日志采用异步记录不影响API响应速度
3. **数据量控制**response_data只记录前1000字符避免存储过大
4. **缓存机制**apiID到featureID的映射在 `ProcessRequests` 时构建,避免频繁查询数据库
5. **失败不收费**根据需求调用失败时天元API不收费所以cost_price为0
## 后续优化建议
1. **定时清理**可以添加定时任务定期清理超过一定时间的日志数据如90天
2. **成本预警**:可以添加监控,当某个时间段的总成本超过阈值时发送告警
3. **成功率监控**:监控各接口的成功率,低于阈值时告警
4. **性能分析**:分析响应时间,找出性能瓶颈
5. **数据归档**:将历史日志归档到其他存储,避免主表过大
## 文件清单
### 新增文件
1. `deploy/sql/tianyuanapi_cost_migration.sql` - 数据库迁移脚本
2. `deploy/sql/generate_tianyuanapi_models.sh` - Model生成脚本
3. `app/main/api/internal/service/tianyuanapiCallLogService.go` - 调用记录服务
4. `deploy/sql/天元API调用记录修改说明.md` - 详细修改说明
5. `deploy/sql/批量替换API调用说明.md` - 批量替换指南
6. `deploy/sql/天元API调用记录功能完成总结.md` - 本文档
### 修改文件
1. `app/main/api/internal/service/apirequestService.go` - 添加调用记录逻辑
2. `app/main/api/internal/svc/servicecontext.go` - 添加新Model和Service初始化
### 待生成文件(执行脚本后)
1. `app/main/model/featureModel_gen.go` - 已更新
2. `app/main/model/featureModel.go` - 已更新
3. `app/main/model/tianyuanapiCallLogModel_gen.go` - 新建
4. `app/main/model/tianyuanapiCallLogModel.go` - 新建

View File

@@ -0,0 +1,83 @@
#!/bin/bash
# 批量修改API请求方法签名添加ctx参数
FILE="app/main/api/internal/service/apirequestService.go"
# 需要修改的方法列表不包括ProcessYYSYBE08Request和ProcessFLXG0V4BRequest因为已经修改过了
METHODS=(
"ProcessPersonEnterpriseProRequest"
"ProcessFLXG0687Request"
"ProcessFLXG3D56Request"
"ProcessIVYZ5733Request"
"ProcessIVYZ9A2BRequest"
"ProcessJRZQ0A03Request"
"ProcessJRZQ8203Request"
"ProcessJRZQ4AA8Request"
"ProcessQYGL8271Request"
"ProcessQYGL6F2DRequest"
"ProcessQCXG7A2BRequest"
"ProcessYYSY09CDRequest"
"ProcessBehaviorRiskScanRequest"
"ProcessDWBG8B4DRequest"
"ProcessDWBG6A2CRequest"
"ProcessJRZQ4B6CRequest"
"ProcessJRZQ09J8Request"
"ProcessJRZQ5E9FRequest"
"ProcessQYGL3F8ERequest"
"ProcessIVYZ81NCRequest"
"ProcessIVYZ7F3ARequest"
"ProcessDWBG7F3ARequest"
"ProcessJRZQ8A2DRequest"
"ProcessYYSY8B1CRequest"
"ProcessYYSY7D3ERequest"
"ProcessFLXG7E8FRequest"
"ProcessIVYZ8I9JRequest"
"ProcessJRZQ7F1ARequest"
"ProcessIVYZ3P9MRequest"
"ProcessJRZQ6F2ARequest"
)
echo "开始批量修改方法签名..."
echo ""
# 备份文件
if [ ! -f "$FILE.backup" ]; then
cp "$FILE" "$FILE.backup"
echo "✓ 已备份文件到: $FILE.backup"
echo ""
else
echo "⚠ 备份文件已存在,跳过备份"
echo ""
fi
# 逐个修改方法
count=0
for method in "${METHODS[@]}"; do
# 查找方法定义
old_pattern="func (a \*ApiRequestService) $method(params \[\]byte\) \(\[\]byte, error\)"
new_pattern="func (a *ApiRequestService) $method(ctx context.Context, params \[\]byte\) \(\[\]byte, error\)"
# 检查文件中是否包含这个方法签名
if grep -q "$method" "$FILE"; then
# 使用sed替换
sed -i "s/$old_pattern/$new_pattern/g" "$FILE"
echo "✓ 已修改: $method"
((count++))
else
echo "✗ 未找到: $method"
fi
done
echo ""
echo "========================================="
echo "修改完成!"
echo "========================================="
echo "共修改了 $count 个方法"
echo ""
echo "下一步:"
echo "1. 检查修改后的文件"
echo "2. 编译项目验证是否有错误"
echo "3. 运行测试"
echo "========================================="

View File

@@ -0,0 +1,86 @@
# 批量替换API调用方法的PowerShell脚本
# 说明此脚本用于批量修改apirequestService.go中的API调用方法
# 使用前请先备份文件!
$filePath = "app\main\api\internal\service\apirequestService.go"
# 检查文件是否存在
if (-not (Test-Path $filePath)) {
Write-Host "错误:文件不存在 - $filePath" -ForegroundColor Red
exit 1
}
# 备份文件
$backupPath = "$filePath.backup"
Copy-Item $filePath $backupPath
Write-Host "已备份文件到: $backupPath" -ForegroundColor Green
# 读取文件内容
$content = Get-Content $filePath -Raw
# 需要修改的方法列表(已完成的除外)
$methodsToSkip = @(
"ProcessYYSYBE08Request",
"ProcessFLXG0V4BRequest"
)
# 需要添加ctx声明的模式
$pattern1 = '(?m)(func \(a \*ApiRequestService\) Process(\w+)Request\(params \[\]byte\) \(\[\]byte, error\) \{)'
# 替换API调用
$pattern2 = 'a\.tianyuanapi\.CallInterface\("'
$replacement2 = 'a.callTianyuanApiWithLog(ctx, "", "'
# 执行替换
$count = 0
$content -replace $pattern2, $replacement2 | Out-File $filePath -Encoding UTF8
# 统计替换次数
$matches = [regex]::Matches($content, $pattern2)
$count = $matches.Count
Write-Host "已替换 $count 处 a.tianyuanapi.CallInterface 调用" -ForegroundColor Green
Write-Host "`n现在需要手动为每个方法添加 ctx 声明:" -ForegroundColor Yellow
Write-Host "在每个方法的 func 声明后,第一行添加:" -ForegroundColor Yellow
Write-Host 'ctx := context.Background()' -ForegroundColor Cyan
Write-Host "`n需要修改的方法列表(已完成的除外):" -ForegroundColor Yellow
Write-Host "ProcessPersonEnterpriseProRequest - 主接口和涉诉查询" -ForegroundColor White
Write-Host "ProcessFLXG0687Request" -ForegroundColor White
Write-Host "ProcessFLXG3D56Request" -ForegroundColor White
Write-Host "ProcessIVYZ5733Request" -ForegroundColor White
Write-Host "ProcessIVYZ9A2BRequest" -ForegroundColor White
Write-Host "ProcessJRZQ0A03Request" -ForegroundColor White
Write-Host "ProcessJRZQ8203Request" -ForegroundColor White
Write-Host "ProcessJRZQ4AA8Request" -ForegroundColor White
Write-Host "ProcessQYGL8271Request" -ForegroundColor White
Write-Host "ProcessQYGL6F2DRequest" -ForegroundColor White
Write-Host "ProcessQCXG7A2BRequest" -ForegroundColor White
Write-Host "ProcessYYSY09CDRequest" -ForegroundColor White
Write-Host "ProcessDWBG8B4DRequest" -ForegroundColor White
Write-Host "ProcessDWBG6A2CRequest" -ForegroundColor White
Write-Host "ProcessJRZQ4B6CRequest" -ForegroundColor White
Write-Host "ProcessJRZQ09J8Request" -ForegroundColor White
Write-Host "ProcessJRZQ5E9FRequest" -ForegroundColor White
Write-Host "ProcessQYGL3F8ERequest" -ForegroundColor White
Write-Host "ProcessIVYZ81NCRequest" -ForegroundColor White
Write-Host "ProcessIVYZ7F3ARequest" -ForegroundColor White
Write-Host "ProcessDWBG7F3ARequest" -ForegroundColor White
Write-Host "ProcessJRZQ8A2DRequest" -ForegroundColor White
Write-Host "ProcessYYSY8B1CRequest" -ForegroundColor White
Write-Host "ProcessYYSY7D3ERequest" -ForegroundColor White
Write-Host "ProcessFLXG7E8FRequest" -ForegroundColor White
Write-Host "ProcessIVYZ8I9JRequest" -ForegroundColor White
Write-Host "ProcessJRZQ7F1ARequest" -ForegroundColor White
Write-Host "ProcessIVYZ3P9MRequest" -ForegroundColor White
Write-Host "ProcessJRZQ6F2ARequest" -ForegroundColor White
Write-Host "`n总计需要修改 30 个方法" -ForegroundColor Yellow
Write-Host "`n提示在IDE中使用查找替换功能会更方便" -ForegroundColor Cyan
Write-Host "查找a.tianyuanapi.CallInterface(`(" -ForegroundColor Cyan
Write-Host "替换a.callTianyuanApiWithLog(ctx, `", `(" -ForegroundColor Cyan
Write-Host "`n`n操作完成!请检查文件并手动添加 ctx 声明" -ForegroundColor Green

View File

@@ -0,0 +1,97 @@
# 批量替换天元API调用方法
## 目的
将所有 `a.tianyuanapi.CallInterface(` 替换为 `a.callTianyuanApiWithLog(ctx, "", `
## 需要修改的文件
`ycc-proxy-server/app/main/api/internal/service/apirequestService.go`
## 手动修改步骤
### 步骤1在方法开头添加 ctx 声明
在每个需要修改的方法开头,在 `func` 声明后添加:
```go
ctx := context.Background()
```
### 步骤2替换调用
查找并替换:
```
a.callTianyuanApiWithLog(ctx, "", "API名称", map[string]interface{}{
```
替换为:
```
a.callTianyuanApiWithLog(ctx, "", "API名称", map[string]interface{}{
```
## 具体需要修改的方法列表(已完成部分)
### ✅ 已完成
1. ✅ ProcessYYSYBE08Request (第886行)
2. ✅ ProcessFLXG0V4BRequest (第653行)
### ⏳ 待修改
1. ProcessPersonEnterpriseProRequest (第320行) - 主接口
2. ProcessPersonEnterpriseProRequest (第522行) - 涉诉查询
3. ProcessFLXG0687Request (第675行)
4. ProcessFLXG3D56Request (第705行)
5. ProcessIVYZ5733Request (第759行)
6. ProcessIVYZ9A2BRequest (第815行)
7. ProcessJRZQ0A03Request (第929行)
8. ProcessJRZQ8203Request (第986行)
9. ProcessJRZQ4AA8Request (第1043行)
10. ProcessQYGL8271Request (第1092行)
11. ProcessQYGL6F2DRequest (第1150行)
12. ProcessQCXG7A2BRequest (第1191行)
13. ProcessYYSY09CDRequest (第1211行)
14. ProcessDWBG8B4DRequest (第1322行)
15. ProcessDWBG6A2CRequest (第1347行)
16. ProcessJRZQ4B6CRequest (第1371行)
17. ProcessJRZQ09J8Request (第1395行)
18. ProcessJRZQ5E9FRequest (第1419行)
19. ProcessQYGL3F8ERequest (第1441行)
20. ProcessIVYZ81NCRequest (第1461行)
21. ProcessIVYZ7F3ARequest (第1482行)
22. ProcessDWBG7F3ARequest (第1505行)
23. ProcessJRZQ8A2DRequest (第1528行)
24. ProcessYYSY8B1CRequest (第1550行)
25. ProcessYYSY7D3ERequest (第1569行)
26. ProcessFLXG7E8FRequest (第1590行)
27. ProcessIVYZ8I9JRequest (第1545行)
28. ProcessJRZQ7F1ARequest (第1568行)
29. ProcessIVYZ3P9MRequest (第1591行)
30. ProcessJRZQ6F2ARequest (第1612行)
## 使用IDE批量替换功能
### 方式1使用VSCode查找替换
1. 打开 `apirequestService.go`
2.`Ctrl+H` 打开查找替换
3. 查找内容:`a.callTianyuanApiWithLog(ctx, "", "`
4. 替换内容:`a.callTianyuanApiWithLog(ctx, "", "`
5. 点击"全部替换"
### 方式2使用正则表达式批量替换
查找:`a\.tianyuanapi\.CallInterface\("(.+?)",`
替换:`a.callTianyuanApiWithLog(ctx, "", "$1",`
**注意:** 执行批量替换后,还需要在每个方法开头添加 `ctx := context.Background()` 声明
## 验证修改
修改完成后,执行以下步骤验证:
1. 编译项目
```bash
cd ycc-proxy-server
go build ./...
```
2. 检查是否有编译错误
3. 运行测试发起几个API请求
4. 检查数据库 `tianyuanapi_call_log` 表,验证记录是否正确插入
5. 检查成本价是否正确计算成功调用有成本失败调用成本为0