Files
qnc-server-v3/app/main/api/internal/service/whitelistService.go
2026-03-02 12:46:26 +08:00

451 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}