Files
qnc-server-v3/app/main/api/internal/service/whitelistService.go

451 lines
14 KiB
Go
Raw Normal View History

2026-03-02 12:46:26 +08:00
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
}