fix: 启用服务号回调、协议强制签署校验、消息会话数据修复配套接口

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-06-27 02:38:14 +08:00
parent f6152204d3
commit c26ddfa8d8
8 changed files with 115 additions and 2 deletions

View File

@@ -2522,6 +2522,11 @@ class ShangjiaPaifaView(APIView):
def post(self, request):
logger.info(f"商家派单请求: user_id={request.user.yonghuid}, data={request.data}")
from peizhi.xieyi_service import check_xieyi_or_response
blocked = check_xieyi_or_response(request.user.yonghuid, 'shangjia')
if blocked:
return blocked
try:
data = request.data
@@ -4137,6 +4142,11 @@ class QiangdanView(APIView):
if not dingdan_id:
return Response({'code': 400, 'msg': '订单ID不能为空'}, status=400)
from peizhi.xieyi_service import check_xieyi_or_response
blocked = check_xieyi_or_response(request.user.yonghuid, 'dashou')
if blocked:
return blocked
chat_success = False
try:
with transaction.atomic():

View File

@@ -39,6 +39,7 @@ def _serialize_admin(item):
'content_text': item.content_text or '',
'version': item.version,
'is_active': item.is_active,
'must_sign': item.must_sign,
'sort_order': item.sort_order,
'images': [
{'id': img.id, 'image_path': img.image_path, 'sort_order': img.sort_order}
@@ -104,6 +105,7 @@ class XieyiModifyAPIView(APIView):
content_title=request.data.get('content_title', ''),
content_text=request.data.get('content_text', ''),
is_active=bool(request.data.get('is_active', True)),
must_sign=bool(request.data.get('must_sign', True)),
sort_order=int(request.data.get('sort_order', 0)),
)
return Response({'code': 0, 'msg': '创建成功', 'data': _serialize_admin(item)})
@@ -137,6 +139,8 @@ class XieyiModifyAPIView(APIView):
content_changed = True
if 'is_active' in request.data:
item.is_active = bool(request.data.get('is_active'))
if 'must_sign' in request.data:
item.must_sign = bool(request.data.get('must_sign'))
if 'sort_order' in request.data:
item.sort_order = int(request.data.get('sort_order', 0))
if 'bump_version' in request.data and request.data.get('bump_version'):

View File

@@ -0,0 +1,16 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('peizhi', '0017_xcx_xieyi'),
]
operations = [
migrations.AddField(
model_name='xcxxieyi',
name='must_sign',
field=models.BooleanField(default=True, verbose_name='是否强制签署'),
),
]

View File

@@ -748,6 +748,7 @@ class XcxXieyi(models.Model):
content_text = models.TextField(blank=True, default='', verbose_name='文字内容')
version = models.PositiveIntegerField(default=1, verbose_name='版本号')
is_active = models.BooleanField(default=True, verbose_name='是否启用')
must_sign = models.BooleanField(default=True, verbose_name='是否强制签署')
sort_order = models.IntegerField(default=0, verbose_name='排序')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')

View File

@@ -11,7 +11,7 @@ from .xcx_config_views import (
XcxSysPeizhiQueryView, XcxSysPeizhiUpdateView,
XcxPageZiyuanQueryView, XcxPageZiyuanUpdateView, XcxPageZiyuanUploadView,
)
from .xieyi_views import XieyiListView, XieyiDetailView, XieyiSignStatusView, XieyiSignView
from .xieyi_views import XieyiListView, XieyiDetailView, XieyiSignStatusView, XieyiSignView, XieyiCheckView
urlpatterns = [
path('shangpingonggao/', ShangpinGonggaoView.as_view(), name='商品公告轮播获取'),
@@ -55,6 +55,7 @@ urlpatterns = [
path('xcxyzsc', XcxPageZiyuanUploadView.as_view(), name='管理员上传页面资源'),
path('xieyihq', XieyiListView.as_view(), name='小程序获取协议列表'),
path('xieyixq', XieyiDetailView.as_view(), name='小程序获取协议详情'),
path('xieyijc', XieyiCheckView.as_view(), name='小程序检查协议签署'),
path('xieyizt', XieyiSignStatusView.as_view(), name='小程序查询协议签署状态'),
path('xieyiqm', XieyiSignView.as_view(), name='小程序签署协议'),
]

56
peizhi/xieyi_service.py Normal file
View File

@@ -0,0 +1,56 @@
"""协议签署校验 — 供接单/派单等接口调用"""
from rest_framework.response import Response
from peizhi.models import XcxXieyi, XcxXieyiQianshu
XIEYI_BLOCK_CODE = 4601
def get_unsigned_xieyi_list(yonghuid: str, shenfen: str):
"""返回未签署当前版本的必签协议列表"""
if not yonghuid or not shenfen:
return []
active = XcxXieyi.objects.filter(
shenfen=shenfen, is_active=True, must_sign=True,
).order_by('sort_order', 'id')
unsigned = []
for xieyi in active:
signed = XcxXieyiQianshu.objects.filter(
yonghuid=yonghuid,
xieyi_id=xieyi.id,
xieyi_version=xieyi.version,
).exists()
if not signed:
unsigned.append(xieyi)
return unsigned
def serialize_unsigned_brief(xieyi_list):
return [
{
'id': x.id,
'title': x.title,
'shenfen': x.shenfen,
'version': x.version,
}
for x in xieyi_list
]
def xieyi_required_response(unsigned_list):
return Response({
'code': XIEYI_BLOCK_CODE,
'msg': '请先阅读并签署用户协议',
'data': {
'all_signed': False,
'unsigned': serialize_unsigned_brief(unsigned_list),
},
})
def check_xieyi_or_response(yonghuid: str, shenfen: str):
"""未签署则返回 Response已签署返回 None"""
unsigned = get_unsigned_xieyi_list(yonghuid, shenfen)
if unsigned:
return xieyi_required_response(unsigned)
return None

View File

@@ -10,6 +10,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView
from peizhi.models import XcxXieyi, XcxXieyiImage, XcxXieyiQianshu
from peizhi.xieyi_service import get_unsigned_xieyi_list, serialize_unsigned_brief
from utils.oss_utils import upload_to_oss, validate_image
logger = logging.getLogger(__name__)
@@ -42,6 +43,7 @@ def _serialize_xieyi(item, yonghuid=None, include_images=False):
'content_text': item.content_text or '',
'version': item.version,
'is_active': item.is_active,
'must_sign': item.must_sign,
'sort_order': item.sort_order,
'signed': signed,
}
@@ -91,6 +93,28 @@ class XieyiDetailView(APIView):
})
class XieyiCheckView(APIView):
"""POST /peizhi/xieyijc — 检查必签协议是否已全部签署(后端为准)"""
permission_classes = [permissions.IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
shenfen = (request.data.get('shenfen') or '').strip()
if shenfen not in VALID_SHENFEN:
return Response({'code': 400, 'msg': '无效的身份参数', 'data': None})
yonghuid = request.user.yonghuid
unsigned = get_unsigned_xieyi_list(yonghuid, shenfen)
return Response({
'code': 0,
'msg': 'success',
'data': {
'all_signed': len(unsigned) == 0,
'unsigned': serialize_unsigned_brief(unsigned),
},
})
class XieyiSignStatusView(APIView):
"""POST /peizhi/xieyizt — 是否已签署当前版本"""
@@ -176,5 +200,6 @@ class XieyiSignView(APIView):
'id': record.id,
'signature_path': oss_path,
'signed_at': record.signed_at.strftime('%Y-%m-%d %H:%M:%S'),
'all_signed': len(get_unsigned_xieyi_list(yonghuid, item.shenfen)) == 0,
},
})

View File

@@ -82,7 +82,7 @@ urlpatterns = [
# 打手申诉接口
path('dscfss', DashouShensuView.as_view(), name='打手处罚上诉申请'),
path('sjcfjlhq', ShangjiaChufaJiluHuoquView.as_view(), name='商家处罚记录获取'),
#path('officialcallback', csrf_exempt(WeixinOfficialCallbackView.as_view()), name='用户关注取关服务号回调'),
path('officialcallback', csrf_exempt(WeixinOfficialCallbackView.as_view()), name='用户关注取关服务号回调'),
path('kefujinru', KefuLoginView.as_view(), name='客服登录'),
path('kfjrhqzl', KefuStatsView.as_view(), name='客服获取数据'),