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

Firestore中Campaign编辑删除功能异常:文档存在却提示无文档

问题

开发Android应用时,使用CreateCampaignActivity创建Campaign并存储到Firestore后,在EditCampaignActivity中执行编辑和删除操作均失败。执行编辑操作时,Firestore提示不存在要更新的文档,但该文档实际存在于Firestore数据库中。

相关代码

1. 创建Campaign的Activity代码

class CreateCampaignActivity : AppCompatActivity() {

    private lateinit var startDateTimeTextView: TextView
    private lateinit var endDateTimeTextView: TextView
    private lateinit var titleEditText: TextInputEditText
    private lateinit var db: FirebaseFirestore

    private lateinit var candidate1Text: TextInputEditText
    private lateinit var candidate2Text: TextInputEditText
    private lateinit var candidate3Text: TextInputEditText
    private lateinit var addCampaignButton : Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_create_campaign)
        supportActionBar!!.setDisplayHomeAsUpEnabled(true)
        supportActionBar!!.title = "Create New Campaign"

        titleEditText = findViewById(R.id.textTitle)
        startDateTimeTextView = findViewById(R.id.start_date_time)
        endDateTimeTextView = findViewById(R.id.end_date_time)
        addCampaignButton = findViewById(R.id.btnAddCampaign)

        candidate1Text = findViewById(R.id.textCandidate1)
        candidate2Text = findViewById(R.id.textCandidate2)
        candidate3Text = findViewById(R.id.textCandidate3)

        db = FirebaseFirestore.getInstance()

        addCampaignButton.setOnClickListener {
            val title = titleEditText.text.toString().trim()
            val startDateTime = startDateTimeTextView.text.toString().trim()
            val endDateTime = endDateTimeTextView.text.toString().trim()
            val candidate1Name = candidate1Text.text.toString().trim()
            val candidate2Name = candidate2Text.text.toString().trim()
            val candidate3Name = candidate3Text.text.toString().trim()

            if (title.isEmpty() || startDateTime.isEmpty() || endDateTime.isEmpty() || candidate1Name.isEmpty() || candidate2Name.isEmpty() || candidate3Name.isEmpty()) {
                Toast.makeText(this@CreateCampaignActivity, "Please fill up empty fields", Toast.LENGTH_SHORT).show()
            } else if (!isEndTimeValid(startDateTime, endDateTime)) {
                Toast.makeText(this@CreateCampaignActivity, "The end time must be after start time", Toast.LENGTH_SHORT).show()
            } else {
                val format = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US)
                val startTimestamp = Timestamp(format.parse(startDateTime)!!)
                val endTimestamp = Timestamp(format.parse(endDateTime)!!)
                val campaign = Campaign(
                    id = UUID.randomUUID().toString(),
                    title = title,
                    startDateTime = startTimestamp,
                    endDateTime = endTimestamp,
                    candidate1 = Candidate(
                        id = UUID.randomUUID().toString(),
                        name = candidate1Name
                    ),
                    candidate2 = Candidate(
                        id = UUID.randomUUID().toString(),
                        name = candidate2Name
                    ),
                    candidate3 = Candidate(id = UUID.randomUUID().toString(), name = candidate3Name)
                )
                db.collection("campaigns")
                    .add(campaign)
                    .addOnSuccessListener { documentReference ->
                        campaign.id = documentReference.id
                        Log.d(
                            TAG,
                            "Campaign successfully added to Firestore with ID: ${documentReference.id}"
                        )
                        Toast.makeText(this@CreateCampaignActivity, "Campaign successfully added", Toast.LENGTH_SHORT).show()
                        titleEditText.setText("")
                        startDateTimeTextView.text = ""
                        endDateTimeTextView.text = ""
                        candidate1Text.setText("")
                        candidate2Text.setText("")
                        candidate3Text.setText("")
                        finish()
                    }
                    .addOnFailureListener { e ->
                        Log.w(TAG, "Error adding campaign to Firestore", e)
                        Toast.makeText(this@CreateCampaignActivity, "Error adding campaign", Toast.LENGTH_SHORT).show()
                    }
            }

            startDateTimeTextView.setOnClickListener {
                showStartDateTimeDialog()
            }

            endDateTimeTextView.setOnClickListener {
                showEndDateTimeDialog()
            }
        }
    }
    private fun showStartDateTimeDialog() {
        val calendar = Calendar.getInstance()
        val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, day ->
            calendar.set(Calendar.YEAR, year)
            calendar.set(Calendar.MONTH, month)
            calendar.set(Calendar.DAY_OF_MONTH, day)
            val timeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute ->
                calendar.set(Calendar.HOUR_OF_DAY, hour)
                calendar.set(Calendar.MINUTE, minute)
                startDateTimeTextView.text =
                    SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(calendar.time)
            }
            TimePickerDialog(
                this@CreateCampaignActivity,
                timeSetListener,
                calendar.get(Calendar.HOUR_OF_DAY),
                calendar.get(Calendar.MINUTE),
                true
            ).show()
        }
        DatePickerDialog(
            this@CreateCampaignActivity,
            dateSetListener,
            calendar.get(Calendar.YEAR),
            calendar.get(Calendar.MONTH),
            calendar.get(Calendar.DAY_OF_MONTH)
        ).show()
    }

    private fun showEndDateTimeDialog() {
        val calendar = Calendar.getInstance()
        val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, day ->
            calendar.set(Calendar.YEAR, year)
            calendar.set(Calendar.MONTH, month)
            calendar.set(Calendar.DAY_OF_MONTH, day)
            val timeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute ->
                calendar.set(Calendar.HOUR_OF_DAY, hour)
                calendar.set(Calendar.MINUTE, minute)
                endDateTimeTextView.text =
                    SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(calendar.time)
            }
            TimePickerDialog(
                this@CreateCampaignActivity,
                timeSetListener,
                calendar.get(Calendar.HOUR_OF_DAY),calendar.get(Calendar.MINUTE),
                true
            ).show()
        }
        DatePickerDialog(
            this@CreateCampaignActivity,
            dateSetListener,
            calendar.get(Calendar.YEAR),
            calendar.get(Calendar.MONTH),
            calendar.get(Calendar.DAY_OF_MONTH)
        ).show()
    }

    private fun isEndTimeValid(startDateTime: String, endDateTime: String): Boolean {
        val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault())
        val startDate = sdf.parse(startDateTime)
        val endDate = sdf.parse(endDateTime)
        return endDate?.after(startDate) ?: false
    }

    companion object {
        private const val TAG = "CreateCampaignActivity"
    }
}

2. 编辑Campaign的Activity代码

class EditCampaignActivity : AppCompatActivity() {

    private lateinit var datetimeTextView: TextView
    private lateinit var titleEditText: TextInputEditText
    private lateinit var db: FirebaseFirestore

    private lateinit var candidate1Text: TextInputEditText
    private lateinit var candidate2Text: TextInputEditText
    private lateinit var candidate3Text: TextInputEditText
    private lateinit var editCampaign : Button
    private lateinit var delCampaign: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_edit_campaign)
        supportActionBar!!.setDisplayHomeAsUpEnabled(true)
        supportActionBar!!.title = "Edit Campaign"

        titleEditText = findViewById(R.id.textTitle)
        datetimeTextView = findViewById(R.id.DateTime)
        delCampaign = findViewById(R.id.btndelCampaign)
        editCampaign = findViewById(R.id.btnSaveCampaign)
        db = FirebaseFirestore.getInstance()

        candidate1Text = findViewById(R.id.textCandidate1)
        candidate2Text = findViewById(R.id.textCandidate2)
        candidate3Text = findViewById(R.id.textCandidate3)

        intent.getStringExtra("CAMPAIGN_ID")
        val campaign = intent.getParcelableExtra<Campaign>("CAMPAIGN_DATA")

        if (campaign != null) {
            intent.putExtra("CAMPAIGN_ID", campaign.id)
        }
        if (campaign != null) {
            titleEditText.setText(campaign.title)
        }
        if (campaign != null) {
            datetimeTextView.text = campaign.endDateTime.toDate().toString()
        }
        if (campaign != null) {
            candidate1Text.setText(campaign.candidate1.name)
        }
        if (campaign != null) {
            candidate2Text.setText(campaign.candidate2.name)
        }
        if (campaign != null) {
            candidate3Text.setText(campaign.candidate3.name)
        }

        //Update function
        editCampaign.setOnClickListener {
            val updatedTitle = titleEditText.text.toString()
            val updatedCandidate1 = candidate1Text.text.toString()
            val updatedCandidate2 = candidate2Text.text.toString()
            val updatedCandidate3 = candidate3Text.text.toString()

            campaign?.apply {
                title = updatedTitle
                endDateTime = Timestamp.now()
                candidate1.name = updatedCandidate1
                candidate2.name = updatedCandidate2
                candidate3.name = updatedCandidate3
            }

            if (campaign != null) {
                db.collection("campaigns").document(campaign.id)
                    .update(mapOf(
                        "title" to updatedTitle,
                        "endDateTime" to Timestamp.now(),
                        "candidate1" to mapOf(
                            "name" to updatedCandidate1
                        ),
                        "candidate2" to mapOf(
                            "name" to updatedCandidate2
                        ),
                        "candidate3" to mapOf(
                            "name" to updatedCandidate3
                        )
                    ))
                    .addOnSuccessListener {
                        Toast.makeText(this, "Campaign updated", Toast.LENGTH_SHORT).show()
                    }
                    .addOnFailureListener {
                        Toast.makeText(this, "Error updating campaign: $it", Toast.LENGTH_SHORT).show()
                    }
            }
            AlertDialog.Builder(this)
                .setTitle("Edit Campaign")
                .setMessage("Are you sure you want to save the changes to this campaign?")
                .setPositiveButton("Yes") { _, _ ->
                    finish()
                }
                .setNegativeButton("No", null)
                .show()
        }

       // perform delete function
        delCampaign.setOnClickListener {
            AlertDialog.Builder(this)
                .setTitle("Delete Campaign")
                .setMessage("Are you sure you want to delete this campaign?")
                .setPositiveButton("Yes") { _, _ ->
                    if (campaign != null) {
                        deleteCampaign(campaign)
                    }
                }
                .setNegativeButton("No", null)
                .show()
        }
    }

    private fun deleteCampaign(campaign: Campaign) {
        db.collection("campaigns").document(campaign.id).delete()
            .addOnSuccessListener {
                finish()
            }
            .addOnFailureListener { e ->
                Toast.makeText(this, "Error deleting campaign: ${e.message}", Toast.LENGTH_SHORT).show()
            }
    }
}

问题原因

核心问题是Campaign的ID与Firestore文档ID不匹配:

  1. 创建Campaign时,先给campaign.id赋值UUID,再调用add()方法存入Firestore。add()会自动生成文档ID,但此时文档内的id字段仍是你生成的UUID。
  2. 你在addOnSuccessListener里将本地campaign.id改为Firestore生成的文档ID,但该修改仅在内存中,未同步到Firestore文档,导致Firestore中存储的Campaign对象id字段依然是UUID。
  3. 从Firestore查询Campaign并传入EditCampaignActivity后,campaign.id是UUID,用这个ID调用document(campaign.id)查找文档时,实际Firestore文档的ID是自动生成的字符串,自然找不到对应文档,导致编辑、删除失败。

解决方案

有两种可行方案:

方案一:用UUID作为Firestore文档ID(推荐)

创建Campaign时直接指定文档ID为UUID,确保文档ID与Campaign的id字段一致:

// 生成UUID作为文档ID和Campaign的id
val campaignId = UUID.randomUUID().toString()
val campaign = Campaign(
    id = campaignId,
    title = title,
    startDateTime = startTimestamp,
    endDateTime = endTimestamp,
    candidate1 = Candidate(
        id = UUID.randomUUID().toString(),
        name = candidate1Name
    ),
    candidate2 = Candidate(
        id = UUID.randomUUID().toString(),
        name = candidate2Name
    ),
    candidate3 = Candidate(id = UUID.randomUUID().toString(), name = candidate3Name)
)
// 用document()指定ID,再调用set()存入
db.collection("campaigns")
    .document(campaignId)
    .set(campaign)
    .addOnSuccessListener {
        Log.d(TAG, "Campaign successfully added to Firestore with ID: $campaignId")
        Toast.makeText(this@CreateCampaignActivity, "Campaign successfully added", Toast.LENGTH_SHORT).show()
        // 清空字段、返回前页等逻辑不变
        titleEditText.setText("")
        startDateTimeTextView.text = ""
        endDateTimeTextView.text = ""
        candidate1Text.setText("")
        candidate2Text.setText("")
        candidate3Text.setText("")
        finish()
    }
    .addOnFailureListener { e ->
        Log.w(TAG, "Error adding campaign to Firestore", e)
        Toast.makeText(this@CreateCampaignActivity, "Error adding campaign", Toast.LENGTH_SHORT).show()
    }

方案二:将Firestore文档ID同步到文档的id字段

如果坚持用add()自动生成文档ID,需在添加成功后将修改后的campaign对象写回Firestore,确保文档内的id字段与文档ID一致:

db.collection("campaigns")
    .add(campaign)
    .addOnSuccessListener { documentReference ->
        campaign.id = documentReference.id
        // 新增这行,将修改后的campaign写回文档
        documentReference.set(campaign)
        Log.d(TAG, "Campaign successfully added to Firestore with ID: ${documentReference.id}")
        Toast.makeText(this@CreateCampaignActivity, "Campaign successfully added", Toast.LENGTH_SHORT).show()
        // 清空字段、返回前页等逻辑不变
        titleEditText.setText("")
        startDateTimeTextView.text = ""
        endDateTimeTextView.text = ""
        candidate1Text.setText("")
        candidate2Text.setText("")
        candidate3Text.setText("")
        finish()
    }
    .addOnFailureListener { e ->
        Log.w(TAG, "Error adding campaign to Firestore", e)
        Toast.makeText(this@CreateCampaignActivity, "Error adding campaign", Toast.LENGTH_SHORT).show()
    }

额外优化点

  1. EditCampaignActivity中,intent.getStringExtra("CAMPAIGN_ID")和intent.putExtra("CAMPAIGN_ID", campaign.id)均为无效代码,直接删除即可,已通过CAMPAIGN_DATA传递完整的Campaign对象。
  2. EditCampaignActivity中编辑时直接将endDateTime设为Timestamp.now(),若需保留修改时间的功能,建议参考CreateCampaignActivity添加日期时间选择器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 20:40:18