package service import ( "context" "database/sql" "encoding/hex" "encoding/json" "strings" "github.com/google/uuid" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/sqlx" "qnc-server/app/main/api/internal/config" "qnc-server/app/main/model" "qnc-server/common/xerr" "qnc-server/pkg/lzkit/crypto" "qnc-server/pkg/lzkit/lzUtils" ) // WhitelistService 白名单领域服务 type WhitelistService struct { config config.Config UserFeatureWhitelistModel model.UserFeatureWhitelistModel WhitelistOrderModel model.WhitelistOrderModel WhitelistOrderItemModel model.WhitelistOrderItemModel QueryModel model.QueryModel FeatureModel model.FeatureModel } // NewWhitelistService 创建白名单服务 func NewWhitelistService( c config.Config, userFeatureWhitelistModel model.UserFeatureWhitelistModel, whitelistOrderModel model.WhitelistOrderModel, whitelistOrderItemModel model.WhitelistOrderItemModel, queryModel model.QueryModel, featureModel model.FeatureModel, ) *WhitelistService { return &WhitelistService{ config: c, UserFeatureWhitelistModel: userFeatureWhitelistModel, WhitelistOrderModel: whitelistOrderModel, WhitelistOrderItemModel: whitelistOrderItemModel, QueryModel: queryModel, FeatureModel: featureModel, } } // EnsureFreeWhitelist 免费下架:如果还没有生效白名单,则创建一条免费白名单记录 func (s *WhitelistService) EnsureFreeWhitelist( ctx context.Context, session sqlx.Session, idCard string, feature *model.Feature, userId string, orderId string, ) error { builder := s.UserFeatureWhitelistModel.SelectBuilder(). Where("id_card = ? AND feature_id = ?", idCard, feature.Id) records, err := s.UserFeatureWhitelistModel.FindAll(ctx, builder, "") if err != nil { return errors.Wrap(err, "查询白名单记录失败") } for _, r := range records { if r.Status == 1 { return nil } } wl := &model.UserFeatureWhitelist{ Id: uuid.NewString(), IdCard: idCard, FeatureId: feature.Id, FeatureApiId: feature.ApiId, UserId: userId, OrderId: lzUtils.StringToNullString(orderId), WhitelistOrderId: lzUtils.StringToNullString(""), Amount: 0, Status: 1, } _, err = s.UserFeatureWhitelistModel.Insert(ctx, session, wl) if err != nil { return errors.Wrap(err, "创建免费白名单记录失败") } return nil } // CreateWhitelistByPaidOrder 根据已支付的白名单订单,创建对应的白名单记录 func (s *WhitelistService) CreateWhitelistByPaidOrder( ctx context.Context, session sqlx.Session, order *model.Order, whitelistOrder *model.WhitelistOrder, ) error { if whitelistOrder.Status != 2 { return nil } itemBuilder := s.WhitelistOrderItemModel.SelectBuilder(). Where("order_id = ?", whitelistOrder.Id) items, err := s.WhitelistOrderItemModel.FindAll(ctx, itemBuilder, "") if err != nil { return errors.Wrap(err, "查询白名单订单明细失败") } for _, item := range items { wl := &model.UserFeatureWhitelist{ Id: uuid.NewString(), IdCard: whitelistOrder.IdCard, FeatureId: item.FeatureId, FeatureApiId: item.FeatureApiId, UserId: whitelistOrder.UserId, OrderId: lzUtils.StringToNullString(order.Id), WhitelistOrderId: lzUtils.StringToNullString(whitelistOrder.Id), Amount: item.Price, Status: 1, } if _, err := s.UserFeatureWhitelistModel.Insert(ctx, session, wl); err != nil { return errors.Wrap(err, "创建白名单记录失败") } } return nil } // GetWhitelistedFeatureApisByIdCard 获取某个身份证号已下架的 feature_api_id 集合 func (s *WhitelistService) GetWhitelistedFeatureApisByIdCard( ctx context.Context, idCard string, ) (map[string]bool, error) { result := make(map[string]bool) if s == nil || idCard == "" { return result, nil } builder := s.UserFeatureWhitelistModel.SelectBuilder(). Where("id_card = ? AND status = ?", idCard, 1) list, err := s.UserFeatureWhitelistModel.FindAll(ctx, builder, "") if err != nil { return nil, errors.Wrap(err, "查询白名单失败") } for _, wl := range list { result[wl.FeatureApiId] = true } return result, nil } // CheckWhitelistExists 检查指定身份证号和模块是否已有生效的白名单记录 func (s *WhitelistService) CheckWhitelistExists( ctx context.Context, idCard string, featureId string, ) (bool, error) { if idCard == "" || featureId == "" { return false, nil } builder := s.UserFeatureWhitelistModel.SelectBuilder(). Where("id_card = ? AND feature_id = ? AND status = ?", idCard, featureId, 1) list, err := s.UserFeatureWhitelistModel.FindAll(ctx, builder, "") if err != nil { return false, errors.Wrap(err, "查询白名单记录失败") } return len(list) > 0, nil } // ProcessOfflineFeature 统一下架处理 func (s *WhitelistService) ProcessOfflineFeature( ctx context.Context, session sqlx.Session, idCard string, featureApiId string, userId string, orderId string, ) (needPay bool, amount float64, whitelistCreated bool, err error) { mainApiId := s.extractMainApiId(featureApiId) feature, err := s.getFeatureByApiId(ctx, mainApiId) if err != nil { return false, 0, false, err } if feature.WhitelistPrice < 0 { return false, 0, false, errors.Wrapf(xerr.NewErrMsg("该模块不支持下架"), "") } exists, err := s.CheckWhitelistExists(ctx, idCard, feature.Id) if err != nil { return false, 0, false, err } if exists { return false, 0, true, nil } price := feature.WhitelistPrice if price <= 0 { if err := s.EnsureFreeWhitelist(ctx, session, idCard, feature, userId, orderId); err != nil { return false, 0, false, err } return false, 0, true, nil } paidOrderId, err := s.findPaidWhitelistOrder(ctx, userId, idCard, feature.Id) if err != nil { return false, 0, false, err } if paidOrderId != "" { if err := s.createWhitelistFromPaidOrder(ctx, session, idCard, feature, userId, orderId, paidOrderId, price); err != nil { return false, 0, false, err } return false, price, true, nil } return true, price, false, nil } func (s *WhitelistService) extractMainApiId(featureApiId string) string { if idx := strings.Index(featureApiId, "_"); idx > 0 { return featureApiId[:idx] } return featureApiId } func (s *WhitelistService) getFeatureByApiId(ctx context.Context, apiId string) (*model.Feature, error) { feature, err := s.FeatureModel.FindOneByApiId(ctx, apiId) if err != nil { if errors.Is(err, model.ErrNotFound) { return nil, errors.Wrap(err, "模块不存在") } return nil, errors.Wrap(err, "查询模块信息失败") } return feature, nil } func (s *WhitelistService) findPaidWhitelistOrder( ctx context.Context, userId string, idCard string, featureId string, ) (string, error) { orderBuilder := s.WhitelistOrderModel.SelectBuilder(). Where("user_id = ? AND id_card = ? AND status = ?", userId, idCard, 2) orders, err := s.WhitelistOrderModel.FindAll(ctx, orderBuilder, "") if err != nil { return "", errors.Wrap(err, "查询白名单订单失败") } for _, order := range orders { itemBuilder := s.WhitelistOrderItemModel.SelectBuilder(). Where("order_id = ? AND feature_id = ?", order.Id, featureId) items, itemErr := s.WhitelistOrderItemModel.FindAll(ctx, itemBuilder, "") if itemErr != nil { return "", errors.Wrap(itemErr, "查询白名单订单明细失败") } if len(items) > 0 { return order.Id, nil } } return "", nil } func (s *WhitelistService) createWhitelistFromPaidOrder( ctx context.Context, session sqlx.Session, idCard string, feature *model.Feature, userId string, orderId string, paidOrderId string, price float64, ) error { wl := &model.UserFeatureWhitelist{ Id: uuid.NewString(), IdCard: idCard, FeatureId: feature.Id, FeatureApiId: feature.ApiId, UserId: userId, OrderId: lzUtils.StringToNullString(orderId), WhitelistOrderId: lzUtils.StringToNullString(paidOrderId), Amount: price, Status: 1, } if _, err := s.UserFeatureWhitelistModel.Insert(ctx, session, wl); err != nil { return errors.Wrap(err, "根据已支付订单创建白名单记录失败") } return nil } // DeleteFeatureFromQueryData 从报告数据中删除指定模块的数据 func (s *WhitelistService) DeleteFeatureFromQueryData( ctx context.Context, session sqlx.Session, queryId string, featureApiId string, ) error { queryModel, err := s.getQueryModel(ctx, queryId) if err != nil { return err } if queryModel == nil { return nil } mainApiId := s.extractMainApiId(featureApiId) dataArray, key, err := s.decryptQueryData(queryModel) if err != nil { return err } modifiedArray, hasModified := s.clearFeatureData(dataArray, mainApiId) if !hasModified { logx.Infof("删除报告数据:查询记录 %s 中未找到模块 %s 的数据,跳过删除", queryId, featureApiId) return nil } if err := s.updateQueryData(ctx, session, queryModel, modifiedArray, key); err != nil { return err } return nil } func (s *WhitelistService) getQueryModel(ctx context.Context, queryId string) (*model.Query, error) { queryModel, err := s.QueryModel.FindOne(ctx, queryId) if err != nil { if errors.Is(err, model.ErrNotFound) { return nil, nil } return nil, errors.Wrap(err, "查询报告记录失败") } if !queryModel.QueryData.Valid || queryModel.QueryData.String == "" { return nil, nil } return queryModel, nil } func (s *WhitelistService) decryptQueryData(queryModel *model.Query) ([]map[string]interface{}, []byte, error) { secretKey := s.config.Encrypt.SecretKey key, decodeErr := hex.DecodeString(secretKey) if decodeErr != nil { return nil, nil, errors.Wrap(decodeErr, "获取AES密钥失败") } decryptedData, decryptErr := crypto.AesDecrypt(queryModel.QueryData.String, key) if decryptErr != nil { return nil, nil, errors.Wrap(decryptErr, "解密报告数据失败") } var dataArray []map[string]interface{} if unmarshalErr := json.Unmarshal(decryptedData, &dataArray); unmarshalErr != nil { return nil, nil, errors.Wrap(unmarshalErr, "解析报告数据失败") } return dataArray, key, nil } func (s *WhitelistService) clearFeatureData(dataArray []map[string]interface{}, mainApiId string) ([]map[string]interface{}, bool) { modifiedArray := make([]map[string]interface{}, 0, len(dataArray)) hasModified := false for _, item := range dataArray { newItem := make(map[string]interface{}) for k, v := range item { newItem[k] = v } apiID, ok := item["apiID"].(string) if !ok { modifiedArray = append(modifiedArray, newItem) continue } if s.extractMainApiId(apiID) == mainApiId { newItem["data"] = nil hasModified = true } modifiedArray = append(modifiedArray, newItem) } return modifiedArray, hasModified } func (s *WhitelistService) updateQueryData( ctx context.Context, session sqlx.Session, queryModel *model.Query, filteredArray []map[string]interface{}, key []byte, ) error { filteredBytes, marshalErr := json.Marshal(filteredArray) if marshalErr != nil { return errors.Wrap(marshalErr, "序列化过滤后的报告数据失败") } encryptedData, encryptErr := crypto.AesEncrypt(filteredBytes, key) if encryptErr != nil { return errors.Wrap(encryptErr, "加密过滤后的报告数据失败") } queryModel.QueryData = sql.NullString{String: encryptedData, Valid: true} if updateErr := s.QueryModel.UpdateWithVersion(ctx, session, queryModel); updateErr != nil { return errors.Wrap(updateErr, "更新报告数据失败") } return nil } // CheckQueryDataContainsFeature 检查报告数据中是否包含指定的模块 func (s *WhitelistService) CheckQueryDataContainsFeature( ctx context.Context, queryId string, featureApiId string, ) (bool, error) { queryModel, err := s.getQueryModel(ctx, queryId) if err != nil { return false, err } if queryModel == nil { return false, nil } mainApiId := s.extractMainApiId(featureApiId) dataArray, _, err := s.decryptQueryData(queryModel) if err != nil { return false, err } for _, item := range dataArray { apiID, ok := item["apiID"].(string) if !ok { continue } if s.extractMainApiId(apiID) == mainApiId { dataValue, exists := item["data"] if !exists || dataValue == nil { return false, nil } return true, nil } } return false, nil } // ProcessPaidWhitelistOrder 处理已支付的白名单订单 func (s *WhitelistService) ProcessPaidWhitelistOrder( ctx context.Context, session sqlx.Session, order *model.Order, whitelistOrder *model.WhitelistOrder, ) error { if whitelistOrder.Status != 2 { return nil } itemBuilder := s.WhitelistOrderItemModel.SelectBuilder(). Where("order_id = ?", whitelistOrder.Id) items, err := s.WhitelistOrderItemModel.FindAll(ctx, itemBuilder, "") if err != nil { return errors.Wrap(err, "查询白名单订单明细失败") } for _, item := range items { wl := &model.UserFeatureWhitelist{ Id: uuid.NewString(), IdCard: whitelistOrder.IdCard, FeatureId: item.FeatureId, FeatureApiId: item.FeatureApiId, UserId: whitelistOrder.UserId, OrderId: lzUtils.StringToNullString(""), WhitelistOrderId: lzUtils.StringToNullString(whitelistOrder.Id), Amount: item.Price, Status: 1, } if _, err := s.UserFeatureWhitelistModel.Insert(ctx, session, wl); err != nil { return errors.Wrap(err, "创建白名单记录失败") } logx.Infof("白名单订单支付成功:订单 %s,模块 %s,已创建白名单记录", whitelistOrder.OrderNo, item.FeatureApiId) } return nil }