304 lines
8.8 KiB
Go
304 lines
8.8 KiB
Go
|
|
package service
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"database/sql"
|
|||
|
|
"encoding/json"
|
|||
|
|
"regexp"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
"ycc-server/app/main/api/internal/config"
|
|||
|
|
tianyuanapi "ycc-server/app/main/api/internal/service/tianyuanapi_sdk"
|
|||
|
|
"ycc-server/app/main/model"
|
|||
|
|
"ycc-server/common/ctxdata"
|
|||
|
|
"ycc-server/common/xerr"
|
|||
|
|
|
|||
|
|
"github.com/pkg/errors"
|
|||
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
var queryWhitelistIdCardPattern = regexp.MustCompile(`^\d{17}[\dXx]$`)
|
|||
|
|
|
|||
|
|
const (
|
|||
|
|
tianyuanQueryWhitelistSuccessCode = 0
|
|||
|
|
tianyuanQueryWhitelistExistsCode = 1013
|
|||
|
|
tianyuanQueryWhitelistNotFoundCode = 1014
|
|||
|
|
tianyuanQueryWhitelistLocalFailCode = -1
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// QueryWhitelistSyncService 天远查询白名单同步服务
|
|||
|
|
type QueryWhitelistSyncService struct {
|
|||
|
|
config config.Config
|
|||
|
|
client *tianyuanapi.Client
|
|||
|
|
opLogModel model.QueryWhitelistOpLogModel
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewQueryWhitelistSyncService(
|
|||
|
|
c config.Config,
|
|||
|
|
client *tianyuanapi.Client,
|
|||
|
|
opLogModel model.QueryWhitelistOpLogModel,
|
|||
|
|
) *QueryWhitelistSyncService {
|
|||
|
|
return &QueryWhitelistSyncService{
|
|||
|
|
config: c,
|
|||
|
|
client: client,
|
|||
|
|
opLogModel: opLogModel,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TrySync 尽力同步天远查询白名单,失败仅记操作日志,不影响主流程
|
|||
|
|
func (s *QueryWhitelistSyncService) TrySync(
|
|||
|
|
ctx context.Context,
|
|||
|
|
name, idCard string,
|
|||
|
|
apiCodes []string,
|
|||
|
|
remark string,
|
|||
|
|
) {
|
|||
|
|
if err := s.Sync(ctx, name, idCard, apiCodes, remark); err != nil {
|
|||
|
|
logx.WithContext(ctx).Errorf("天远查询白名单同步失败(不影响主流程): %v", err)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TrySyncWhitelistOrder 尽力同步白名单订单中的模块到天远
|
|||
|
|
func (s *QueryWhitelistSyncService) TrySyncWhitelistOrder(
|
|||
|
|
ctx context.Context,
|
|||
|
|
whitelistOrder *model.WhitelistOrder,
|
|||
|
|
items []*model.WhitelistOrderItem,
|
|||
|
|
remark string,
|
|||
|
|
) {
|
|||
|
|
if whitelistOrder == nil || len(items) == 0 {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
s.TrySync(ctx, "*", whitelistOrder.IdCard, collectWhitelistOrderApiCodes(items), remark)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Sync 将产品编码同步到天远查询白名单(先追加,规则不存在则创建)
|
|||
|
|
func (s *QueryWhitelistSyncService) Sync(
|
|||
|
|
ctx context.Context,
|
|||
|
|
name, idCard string,
|
|||
|
|
apiCodes []string,
|
|||
|
|
remark string,
|
|||
|
|
) error {
|
|||
|
|
if err := validateQueryWhitelistSyncReq(idCard, apiCodes); err != nil {
|
|||
|
|
s.recordOpLogFailure(ctx, "sync", name, idCard, apiCodes, remark, err.Error(), tianyuanQueryWhitelistLocalFailCode)
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
appendResp, err := s.callMgmt(
|
|||
|
|
"/api/v1/query-whitelist/entries/append",
|
|||
|
|
name,
|
|||
|
|
idCard,
|
|||
|
|
apiCodes,
|
|||
|
|
remark,
|
|||
|
|
)
|
|||
|
|
if err != nil {
|
|||
|
|
s.recordOpLogFailure(ctx, "append", name, idCard, apiCodes, remark, err.Error(), tianyuanQueryWhitelistLocalFailCode)
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
if isTianyuanQueryWhitelistSuccess(appendResp.Code) {
|
|||
|
|
s.recordOpLog(ctx, "append", name, idCard, apiCodes, remark, appendResp)
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
if appendResp.Code != tianyuanQueryWhitelistNotFoundCode {
|
|||
|
|
s.recordOpLog(ctx, "append", name, idCard, apiCodes, remark, appendResp)
|
|||
|
|
return errors.Wrapf(xerr.NewErrMsg(appendResp.Message), "天远查询白名单同步失败(code=%d)", appendResp.Code)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
createResp, err := s.callMgmt(
|
|||
|
|
"/api/v1/query-whitelist/entries",
|
|||
|
|
name,
|
|||
|
|
idCard,
|
|||
|
|
apiCodes,
|
|||
|
|
remark,
|
|||
|
|
)
|
|||
|
|
if err != nil {
|
|||
|
|
s.recordOpLogFailure(ctx, "create", name, idCard, apiCodes, remark, err.Error(), tianyuanQueryWhitelistLocalFailCode)
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
if isTianyuanQueryWhitelistSuccess(createResp.Code) {
|
|||
|
|
s.recordOpLog(ctx, "create", name, idCard, apiCodes, remark, createResp)
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
if createResp.Code == tianyuanQueryWhitelistExistsCode {
|
|||
|
|
retryResp, retryErr := s.callMgmt(
|
|||
|
|
"/api/v1/query-whitelist/entries/append",
|
|||
|
|
name,
|
|||
|
|
idCard,
|
|||
|
|
apiCodes,
|
|||
|
|
remark,
|
|||
|
|
)
|
|||
|
|
if retryErr != nil {
|
|||
|
|
s.recordOpLogFailure(ctx, "append", name, idCard, apiCodes, remark, retryErr.Error(), tianyuanQueryWhitelistLocalFailCode)
|
|||
|
|
return retryErr
|
|||
|
|
}
|
|||
|
|
s.recordOpLog(ctx, "append", name, idCard, apiCodes, remark, retryResp)
|
|||
|
|
if isTianyuanQueryWhitelistSuccess(retryResp.Code) {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
return errors.Wrapf(xerr.NewErrMsg(retryResp.Message), "天远查询白名单同步失败(code=%d)", retryResp.Code)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s.recordOpLog(ctx, "create", name, idCard, apiCodes, remark, createResp)
|
|||
|
|
return errors.Wrapf(xerr.NewErrMsg(createResp.Message), "天远查询白名单同步失败(code=%d)", createResp.Code)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func collectWhitelistOrderApiCodes(items []*model.WhitelistOrderItem) []string {
|
|||
|
|
codes := make([]string, 0, len(items))
|
|||
|
|
seen := make(map[string]bool, len(items))
|
|||
|
|
for _, item := range items {
|
|||
|
|
if item == nil || item.FeatureApiId == "" || seen[item.FeatureApiId] {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
seen[item.FeatureApiId] = true
|
|||
|
|
codes = append(codes, item.FeatureApiId)
|
|||
|
|
}
|
|||
|
|
return codes
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func validateQueryWhitelistSyncReq(idCard string, apiCodes []string) error {
|
|||
|
|
if strings.TrimSpace(idCard) == "" {
|
|||
|
|
return errors.Wrapf(xerr.NewErrMsg("身份证号不能为空"), "")
|
|||
|
|
}
|
|||
|
|
if !queryWhitelistIdCardPattern.MatchString(strings.TrimSpace(idCard)) {
|
|||
|
|
return errors.Wrapf(xerr.NewErrMsg("身份证号格式不正确,需为18位中国大陆身份证号"), "")
|
|||
|
|
}
|
|||
|
|
if len(apiCodes) == 0 {
|
|||
|
|
return errors.Wrapf(xerr.NewErrMsg("请至少选择一个产品编码"), "")
|
|||
|
|
}
|
|||
|
|
for _, code := range apiCodes {
|
|||
|
|
if strings.TrimSpace(code) == "" {
|
|||
|
|
return errors.Wrapf(xerr.NewErrMsg("产品编码不能为空"), "")
|
|||
|
|
}
|
|||
|
|
if code == "*" {
|
|||
|
|
return errors.Wrapf(xerr.NewErrMsg("产品编码不支持通配符 *"), "")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *QueryWhitelistSyncService) callMgmt(
|
|||
|
|
apiPath string,
|
|||
|
|
name, idCard string,
|
|||
|
|
apiCodes []string,
|
|||
|
|
remark string,
|
|||
|
|
) (*tianyuanapi.QueryWhitelistMgmtResponse, error) {
|
|||
|
|
mgmtKey := s.config.Tianyuanapi.WhitelistMgmtKey
|
|||
|
|
if mgmtKey == "" {
|
|||
|
|
return nil, errors.Wrapf(xerr.NewErrMsg("查询白名单管理密钥未配置,请在服务端配置 Tianyuanapi.WhitelistMgmtKey"), "")
|
|||
|
|
}
|
|||
|
|
if s.client == nil {
|
|||
|
|
return nil, errors.Wrapf(xerr.NewErrMsg("天远 API 客户端未初始化"), "")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if strings.TrimSpace(name) == "" {
|
|||
|
|
name = "*"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
payload := map[string]interface{}{
|
|||
|
|
"name": name,
|
|||
|
|
"id_card": strings.TrimSpace(idCard),
|
|||
|
|
"api_codes": apiCodes,
|
|||
|
|
}
|
|||
|
|
if strings.TrimSpace(remark) != "" {
|
|||
|
|
payload["remark"] = strings.TrimSpace(remark)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return s.client.CallQueryWhitelistMgmt(apiPath, payload, mgmtKey)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *QueryWhitelistSyncService) recordOpLogFailure(
|
|||
|
|
ctx context.Context,
|
|||
|
|
action, name, idCard string,
|
|||
|
|
apiCodes []string,
|
|||
|
|
remark, message string,
|
|||
|
|
code int64,
|
|||
|
|
) {
|
|||
|
|
s.insertOpLog(ctx, action, name, idCard, apiCodes, remark, code, message, "", nil)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *QueryWhitelistSyncService) recordOpLog(
|
|||
|
|
ctx context.Context,
|
|||
|
|
action, name, idCard string,
|
|||
|
|
apiCodes []string,
|
|||
|
|
remark string,
|
|||
|
|
apiResp *tianyuanapi.QueryWhitelistMgmtResponse,
|
|||
|
|
) {
|
|||
|
|
if apiResp == nil {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
s.insertOpLog(ctx, action, name, idCard, apiCodes, remark, int64(apiResp.Code), apiResp.Message, apiResp.TransactionID, apiResp.Entry)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *QueryWhitelistSyncService) insertOpLog(
|
|||
|
|
ctx context.Context,
|
|||
|
|
action, name, idCard string,
|
|||
|
|
apiCodes []string,
|
|||
|
|
remark string,
|
|||
|
|
tianyuanCode int64,
|
|||
|
|
tianyuanMessage, transactionID string,
|
|||
|
|
entry *tianyuanapi.QueryWhitelistEntry,
|
|||
|
|
) {
|
|||
|
|
if s.opLogModel == nil {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
operatorUserId, err := ctxdata.GetUidFromCtx(ctx)
|
|||
|
|
if err != nil {
|
|||
|
|
logx.WithContext(ctx).Errorf("记录查询白名单操作日志失败: 获取操作人ID失败, %v", err)
|
|||
|
|
operatorUserId = ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
apiCodesJSON, marshalErr := json.Marshal(apiCodes)
|
|||
|
|
if marshalErr != nil {
|
|||
|
|
logx.WithContext(ctx).Errorf("记录查询白名单操作日志失败: 序列化 api_codes 失败, %v", marshalErr)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
log := &model.QueryWhitelistOpLog{
|
|||
|
|
AdminUserId: operatorUserId,
|
|||
|
|
Action: action,
|
|||
|
|
Name: resolveQueryWhitelistName(name),
|
|||
|
|
IdCard: strings.TrimSpace(idCard),
|
|||
|
|
ApiCodes: string(apiCodesJSON),
|
|||
|
|
TianyuanCode: tianyuanCode,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if strings.TrimSpace(remark) != "" {
|
|||
|
|
log.Remark = sql.NullString{String: strings.TrimSpace(remark), Valid: true}
|
|||
|
|
}
|
|||
|
|
if tianyuanMessage != "" {
|
|||
|
|
log.TianyuanMessage = sql.NullString{String: tianyuanMessage, Valid: true}
|
|||
|
|
}
|
|||
|
|
if transactionID != "" {
|
|||
|
|
log.TransactionId = sql.NullString{String: transactionID, Valid: true}
|
|||
|
|
}
|
|||
|
|
if entry != nil {
|
|||
|
|
if entry.IdCardMasked != "" {
|
|||
|
|
log.IdCardMasked = sql.NullString{String: entry.IdCardMasked, Valid: true}
|
|||
|
|
}
|
|||
|
|
if entry.ID != "" {
|
|||
|
|
log.EntryId = sql.NullString{String: entry.ID, Valid: true}
|
|||
|
|
}
|
|||
|
|
if entry.Status != "" {
|
|||
|
|
log.EntryStatus = sql.NullString{String: entry.Status, Valid: true}
|
|||
|
|
}
|
|||
|
|
if len(entry.ApiCodes) > 0 {
|
|||
|
|
entryCodesJSON, _ := json.Marshal(entry.ApiCodes)
|
|||
|
|
log.EntryApiCodes = sql.NullString{String: string(entryCodesJSON), Valid: true}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if _, insertErr := s.opLogModel.Insert(ctx, nil, log); insertErr != nil {
|
|||
|
|
logx.WithContext(ctx).Errorf("记录查询白名单操作日志失败: %v", insertErr)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func resolveQueryWhitelistName(name string) string {
|
|||
|
|
if strings.TrimSpace(name) == "" {
|
|||
|
|
return "*"
|
|||
|
|
}
|
|||
|
|
return strings.TrimSpace(name)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func isTianyuanQueryWhitelistSuccess(code int) bool {
|
|||
|
|
return code == tianyuanQueryWhitelistSuccessCode
|
|||
|
|
}
|