You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter租车应用Firestore请求式写入:审批/拒绝/修改功能实现咨询

实现方案:基于Bloc+Repository的租车应用车辆审核流程

一、Firestore数据结构调整

先重构Firestore集合,拆分出审核请求和正式车辆数据两个核心集合:

  • car_submissions:存储车主提交的待审核请求,核心字段:
    • carInfo:完整车辆信息(品牌、型号、牌照、租金等,和原车辆字段一致)
    • ownerId:提交请求的车主UID
    • status:审核状态(pending/approved/rejected)
    • adminNotes:管理员审核备注(审批/拒绝时填写)
    • submittedAt:服务器生成的提交时间戳
  • cars:仅存储审核通过的正式车辆数据,保留原车辆字段,新增submissionId关联对应的审核请求

二、Repository层实现

1. 定义抽象Repository接口

统一数据操作入口,隔离业务逻辑与数据源:

abstract class CarSubmissionRepository {
  // 车主提交车辆审核请求
  Future<void> submitCarSubmission(Map<String, dynamic> carInfo, String ownerId);
  // 获取当前车主的所有提交记录
  Stream<List<Map<String, dynamic>>> getOwnerSubmissions(String ownerId);
  // 获取管理员待处理的审核请求列表
  Stream<List<Map<String, dynamic>>> getPendingSubmissions();
  // 管理员更新审核状态(审批/拒绝)
  Future<void> updateSubmissionStatus(String submissionId, String status, String? adminNotes);
  // 管理员修改提交的车辆信息
  Future<void> updateSubmissionCarInfo(String submissionId, Map<String, dynamic> updatedCarInfo);
}

2. Firestore具体实现类

class FirestoreCarSubmissionRepository implements CarSubmissionRepository {
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;

  @override
  Future<void> submitCarSubmission(Map<String, dynamic> carInfo, String ownerId) async {
    await _firestore.collection('car_submissions').add({
      'carInfo': carInfo,
      'ownerId': ownerId,
      'status': 'pending',
      'adminNotes': '',
      'submittedAt': FieldValue.serverTimestamp(),
    });
  }

  @override
  Stream<List<Map<String, dynamic>>> getOwnerSubmissions(String ownerId) {
    return _firestore
        .collection('car_submissions')
        .where('ownerId', isEqualTo: ownerId)
        .snapshots()
        .map((snapshot) => snapshot.docs.map((doc) => doc.data()).toList());
  }

  @override
  Stream<List<Map<String, dynamic>>> getPendingSubmissions() {
    return _firestore
        .collection('car_submissions')
        .where('status', isEqualTo: 'pending')
        .snapshots()
        .map((snapshot) => snapshot.docs.map((doc) => doc.data()).toList());
  }

  @override
  Future<void> updateSubmissionStatus(String submissionId, String status, String? adminNotes) async {
    final docRef = _firestore.collection('car_submissions').doc(submissionId);
    await docRef.update({
      'status': status,
      'adminNotes': adminNotes ?? '',
    });
    // 审批通过时同步车辆数据到正式集合
    if (status == 'approved') {
      final submission = await docRef.get();
      await _firestore.collection('cars').add({
        ...submission.data()!['carInfo'],
        'ownerId': submission.data()!['ownerId'],
        'submissionId': submissionId,
      });
    }
  }

  @override
  Future<void> updateSubmissionCarInfo(String submissionId, Map<String, dynamic> updatedCarInfo) async {
    await _firestore.collection('car_submissions').doc(submissionId).update({
      'carInfo': updatedCarInfo,
    });
  }
}

三、Bloc层实现

拆分车主侧和管理员侧两个独立Bloc,分离不同角色的业务逻辑

1. 车主侧CarSubmissionBloc

事件定义

abstract class CarSubmissionEvent {}

class SubmitCarSubmission extends CarSubmissionEvent {
  final Map<String, dynamic> carInfo;
  final String ownerId;

  SubmitCarSubmission(this.carInfo, this.ownerId);
}

class FetchOwnerSubmissions extends CarSubmissionEvent {
  final String ownerId;

  FetchOwnerSubmissions(this.ownerId);
}

状态定义

abstract class CarSubmissionState {}

class CarSubmissionInitial extends CarSubmissionState {}

class CarSubmissionLoading extends CarSubmissionState {}

class CarSubmissionSuccess extends CarSubmissionState {}

class CarSubmissionError extends CarSubmissionState {
  final String message;

  CarSubmissionError(this.message);
}

class OwnerSubmissionsLoaded extends CarSubmissionState {
  final List<Map<String, dynamic>> submissions;

  OwnerSubmissionsLoaded(this.submissions);
}

Bloc核心逻辑

class CarSubmissionBloc extends Bloc<CarSubmissionEvent, CarSubmissionState> {
  final CarSubmissionRepository repository;

  CarSubmissionBloc(this.repository) : super(CarSubmissionInitial()) {
    on<SubmitCarSubmission>(_onSubmitCarSubmission);
    on<FetchOwnerSubmissions>(_onFetchOwnerSubmissions);
  }

  void _onSubmitCarSubmission(SubmitCarSubmission event, Emitter<CarSubmissionState> emit) async {
    emit(CarSubmissionLoading());
    try {
      await repository.submitCarSubmission(event.carInfo, event.ownerId);
      emit(CarSubmissionSuccess());
    } catch (e) {
      emit(CarSubmissionError(e.toString()));
    }
  }

  void _onFetchOwnerSubmissions(FetchOwnerSubmissions event, Emitter<CarSubmissionState> emit) async {
    emit(CarSubmissionLoading());
    try {
      repository.getOwnerSubmissions(event.ownerId).listen((submissions) {
        emit(OwnerSubmissionsLoaded(submissions));
      });
    } catch (e) {
      emit(CarSubmissionError(e.toString()));
    }
  }
}

2. 管理员侧AdminSubmissionBloc

事件定义

abstract class AdminSubmissionEvent {}

class FetchPendingSubmissions extends AdminSubmissionEvent {}

class ApproveSubmission extends AdminSubmissionEvent {
  final String submissionId;
  final String? adminNotes;

  ApproveSubmission(this.submissionId, this.adminNotes);
}

class RejectSubmission extends AdminSubmissionEvent {
  final String submissionId;
  final String adminNotes;

  RejectSubmission(this.submissionId, this.adminNotes);
}

class EditSubmissionCarInfo extends AdminSubmissionEvent {
  final String submissionId;
  final Map<String, dynamic> updatedCarInfo;

  EditSubmissionCarInfo(this.submissionId, this.updatedCarInfo);
}

状态定义

abstract class AdminSubmissionState {}

class AdminSubmissionInitial extends AdminSubmissionState {}

class AdminSubmissionLoading extends AdminSubmissionState {}

class PendingSubmissionsLoaded extends AdminSubmissionState {
  final List<Map<String, dynamic>> submissions;

  PendingSubmissionsLoaded(this.submissions);
}

class SubmissionActionSuccess extends AdminSubmissionState {}

class AdminSubmissionError extends AdminSubmissionState {
  final String message;

  AdminSubmissionError(this.message);
}

Bloc核心逻辑

class AdminSubmissionBloc extends Bloc<AdminSubmissionEvent, AdminSubmissionState> {
  final CarSubmissionRepository repository;

  AdminSubmissionBloc(this.repository) : super(AdminSubmissionInitial()) {
    on<FetchPendingSubmissions>(_onFetchPendingSubmissions);
    on<ApproveSubmission>(_onApproveSubmission);
    on<RejectSubmission>(_onRejectSubmission);
    on<EditSubmissionCarInfo>(_onEditSubmissionCarInfo);
  }

  void _onFetchPendingSubmissions(FetchPendingSubmissions event, Emitter<AdminSubmissionState> emit) async {
    emit(AdminSubmissionLoading());
    try {
      repository.getPendingSubmissions().listen((submissions) {
        emit(PendingSubmissionsLoaded(submissions));
      });
    } catch (e) {
      emit(AdminSubmissionError(e.toString()));
    }
  }

  void _onApproveSubmission(ApproveSubmission event, Emitter<AdminSubmissionState> emit) async {
    emit(AdminSubmissionLoading());
    try {
      await repository.updateSubmissionStatus(event.submissionId, 'approved', event.adminNotes);
      emit(SubmissionActionSuccess());
    } catch (e) {
      emit(AdminSubmissionError(e.toString()));
    }
  }

  void _onRejectSubmission(RejectSubmission event, Emitter<AdminSubmissionState> emit) async {
    emit(AdminSubmissionLoading());
    try {
      await repository.updateSubmissionStatus(event.submissionId, 'rejected', event.adminNotes);
      emit(SubmissionActionSuccess());
    } catch (e) {
      emit(AdminSubmissionError(e.toString()));
    }
  }

  void _onEditSubmissionCarInfo(EditSubmissionCarInfo event, Emitter<AdminSubmissionState> emit) async {
    emit(AdminSubmissionLoading());
    try {
      await repository.updateSubmissionCarInfo(event.submissionId, event.updatedCarInfo);
      emit(SubmissionActionSuccess());
    } catch (e) {
      emit(AdminSubmissionError(e.toString()));
    }
  }
}

四、UI层集成

车主侧UI

  • 车辆提交页面:用户填写信息后触发SubmitCarSubmission事件,根据Bloc状态展示加载、成功提示或错误信息
  • 提交记录页面:触发FetchOwnerSubmissions事件,展示所有提交的状态(待审核/通过/拒绝)及管理员备注

管理员侧UI

  • 待审核列表页面:触发FetchPendingSubmissions事件,展示所有待审核请求,每条请求提供编辑、审批、拒绝操作按钮
  • 车辆信息编辑弹窗:修改完成后触发EditSubmissionCarInfo事件
  • 审批/拒绝操作:点击对应按钮触发ApproveSubmission或RejectSubmission事件,支持输入审核备注

五、额外优化点

  • 状态通知:结合Firebase Cloud Messaging,当审核状态变更时给车主推送通知
  • 权限控制:通过Firebase Auth自定义声明(setCustomUserClaims)判断用户角色,确保仅管理员能访问审核页面
  • 数据校验:在Repository层添加车辆信息合法性校验,拦截无效提交

内容的提问来源于stack exchange,提问作者xs0mE1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 16:33:31