feat(customer): 网站客户身份签发与长期负责人绑定
可信客户身份由站点后端签发,长期负责人与本次接待人分开保存。
This commit is contained in:
1 parent
2554eb7bc7
commit
ad90b830c4
18 files changed
+3123
No files matched your search
@@ -0,0 +1,88 @@
|
||||
"""
|
||||
Copyright 2024-2026 ChatterMate
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from typing import Optional, Dict, Any
|
||||
from app.repositories.customer import CustomerRepository
|
||||
|
||||
# Where a handoff email goes when the customer row can't take it — another
|
||||
# customer in the org already owns that address (the (email, organization_id)
|
||||
# unique constraint), or it is a channel identity key. meta_data renders as a
|
||||
# label/value list in the inbox customer panel, so an agent still sees the
|
||||
# address and can follow up. Dropping it is how a visitor asks to be contacted
|
||||
# and then never is.
|
||||
CONTACT_EMAIL_META_KEY = "contact_email_provided"
|
||||
|
||||
|
||||
def retain_unstored_email(customer_repo, customer_id, submitted_email: str, update_result: dict) -> bool:
|
||||
"""Keep a submitted handoff email that ``update_contact`` could not apply.
|
||||
|
||||
Returns True when it was retained on meta_data. No-op when the address did
|
||||
land on the customer, when it is already their stored address, or when
|
||||
nothing was submitted — so a normal capture writes no metadata.
|
||||
"""
|
||||
if not submitted_email or update_result.get('email_updated'):
|
||||
return False
|
||||
stored = (update_result.get('email') or '').lower()
|
||||
if submitted_email.lower() == stored:
|
||||
return False
|
||||
return customer_repo.update_meta_data(
|
||||
customer_id, {CONTACT_EMAIL_META_KEY: submitted_email}
|
||||
) is not None
|
||||
|
||||
|
||||
def build_handoff_contact_form(
|
||||
customer,
|
||||
collect_email: bool,
|
||||
collect_name: bool,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Return ``form_data`` (``form_type='contact'``) for the handoff contact prompt, or
|
||||
``None`` when there is nothing to collect (toggles off, or the customer already has the
|
||||
info). The widget renders this with its existing inline form UI.
|
||||
"""
|
||||
if customer is None:
|
||||
return None
|
||||
|
||||
needs_email = bool(collect_email) and CustomerRepository.is_placeholder_email(getattr(customer, 'email', None))
|
||||
full_name = (getattr(customer, 'full_name', None) or '').strip()
|
||||
needs_name = bool(collect_name) and not full_name
|
||||
|
||||
if not needs_email and not needs_name:
|
||||
return None
|
||||
|
||||
fields = []
|
||||
if needs_email:
|
||||
fields.append({
|
||||
'name': 'email',
|
||||
'type': 'email',
|
||||
'label': 'Email',
|
||||
'placeholder': 'you@example.com',
|
||||
'required': True,
|
||||
})
|
||||
if needs_name:
|
||||
fields.append({
|
||||
'name': 'name',
|
||||
'type': 'text',
|
||||
'label': 'Name',
|
||||
'placeholder': 'Your name',
|
||||
'required': False,
|
||||
})
|
||||
|
||||
return {
|
||||
'form_type': 'contact',
|
||||
'title': 'Before we connect you to a teammate',
|
||||
'description': 'Share your details so we can follow up.',
|
||||
'fields': fields,
|
||||
}
|
||||
Reference in new issue
Block a user