206 lines
7.6 KiB
Python
206 lines
7.6 KiB
Python
"""小程序协议 — 查询与签署"""
|
|
import logging
|
|
import time
|
|
import uuid
|
|
|
|
from django.db import transaction
|
|
from rest_framework import permissions
|
|
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
|
|
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__)
|
|
|
|
VALID_SHENFEN = frozenset({'dashou', 'guanshi', 'zuzhang', 'shangjia', 'normal'})
|
|
|
|
|
|
def _client_ip(request):
|
|
xff = request.META.get('HTTP_X_FORWARDED_FOR')
|
|
if xff:
|
|
ip = xff.split(',')[0].strip()
|
|
if ip:
|
|
return ip
|
|
return request.META.get('REMOTE_ADDR', '') or ''
|
|
|
|
|
|
def _serialize_xieyi(item, yonghuid=None, include_images=False):
|
|
signed = False
|
|
if yonghuid:
|
|
signed = XcxXieyiQianshu.objects.filter(
|
|
yonghuid=yonghuid, xieyi_id=item.id, xieyi_version=item.version,
|
|
).exists()
|
|
data = {
|
|
'id': item.id,
|
|
'title': item.title,
|
|
'shenfen': item.shenfen,
|
|
'shenfen_label': item.get_shenfen_display(),
|
|
'mode': item.mode,
|
|
'content_title': item.content_title or '',
|
|
'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,
|
|
}
|
|
if include_images:
|
|
data['images'] = [
|
|
{'id': img.id, 'image_path': img.image_path, 'sort_order': img.sort_order}
|
|
for img in item.images.all().order_by('sort_order', 'id')
|
|
]
|
|
return data
|
|
|
|
|
|
class XieyiListView(APIView):
|
|
"""POST /peizhi/xieyihq — 按身份获取启用协议列表"""
|
|
|
|
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': {'list': []}})
|
|
yonghuid = request.user.yonghuid
|
|
qs = XcxXieyi.objects.filter(shenfen=shenfen, is_active=True).order_by('sort_order', 'id')
|
|
items = [_serialize_xieyi(x, yonghuid) for x in qs]
|
|
return Response({'code': 0, 'msg': 'success', 'data': {'list': items}})
|
|
|
|
|
|
class XieyiDetailView(APIView):
|
|
"""POST /peizhi/xieyixq — 协议详情"""
|
|
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
parser_classes = [JSONParser]
|
|
|
|
def post(self, request):
|
|
xieyi_id = request.data.get('id') or request.data.get('xieyi_id')
|
|
if not xieyi_id:
|
|
return Response({'code': 400, 'msg': '缺少协议 id', 'data': None})
|
|
try:
|
|
item = XcxXieyi.objects.prefetch_related('images').get(id=xieyi_id, is_active=True)
|
|
except XcxXieyi.DoesNotExist:
|
|
return Response({'code': 404, 'msg': '协议不存在或已下线', 'data': None})
|
|
yonghuid = request.user.yonghuid
|
|
return Response({
|
|
'code': 0,
|
|
'msg': 'success',
|
|
'data': _serialize_xieyi(item, yonghuid, include_images=True),
|
|
})
|
|
|
|
|
|
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 — 是否已签署当前版本"""
|
|
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
parser_classes = [JSONParser]
|
|
|
|
def post(self, request):
|
|
xieyi_id = request.data.get('id') or request.data.get('xieyi_id')
|
|
if not xieyi_id:
|
|
return Response({'code': 400, 'msg': '缺少协议 id', 'data': None})
|
|
try:
|
|
item = XcxXieyi.objects.get(id=xieyi_id, is_active=True)
|
|
except XcxXieyi.DoesNotExist:
|
|
return Response({'code': 404, 'msg': '协议不存在', 'data': None})
|
|
yonghuid = request.user.yonghuid
|
|
record = XcxXieyiQianshu.objects.filter(
|
|
yonghuid=yonghuid, xieyi_id=item.id, xieyi_version=item.version,
|
|
).order_by('-signed_at').first()
|
|
return Response({
|
|
'code': 0,
|
|
'msg': 'success',
|
|
'data': {
|
|
'signed': record is not None,
|
|
'version': item.version,
|
|
'signed_at': record.signed_at.strftime('%Y-%m-%d %H:%M:%S') if record else '',
|
|
'signature_path': record.signature_path if record else '',
|
|
},
|
|
})
|
|
|
|
|
|
class XieyiSignView(APIView):
|
|
"""POST /peizhi/xieyiqm — 上传手写签名并完成签署"""
|
|
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
parser_classes = (MultiPartParser, FormParser)
|
|
|
|
def post(self, request):
|
|
xieyi_id = request.data.get('xieyi_id') or request.data.get('id')
|
|
shenfen = (request.data.get('shenfen') or '').strip()
|
|
image_file = request.FILES.get('image') or request.FILES.get('file')
|
|
if not xieyi_id:
|
|
return Response({'code': 1, 'msg': '缺少 xieyi_id', 'data': None})
|
|
if not image_file:
|
|
return Response({'code': 2, 'msg': '请上传签名图片', 'data': None})
|
|
if shenfen and shenfen not in VALID_SHENFEN:
|
|
return Response({'code': 3, 'msg': '无效身份', 'data': None})
|
|
|
|
is_valid, error_msg = validate_image(image_file)
|
|
if not is_valid:
|
|
return Response({'code': 4, 'msg': error_msg, 'data': None})
|
|
|
|
try:
|
|
item = XcxXieyi.objects.get(id=xieyi_id, is_active=True)
|
|
except XcxXieyi.DoesNotExist:
|
|
return Response({'code': 404, 'msg': '协议不存在或已下线', 'data': None})
|
|
|
|
yonghuid = request.user.yonghuid
|
|
if XcxXieyiQianshu.objects.filter(
|
|
yonghuid=yonghuid, xieyi_id=item.id, xieyi_version=item.version,
|
|
).exists():
|
|
return Response({'code': 5, 'msg': '您已签署过当前版本协议', 'data': None})
|
|
|
|
oss_path = f'qianming/xieyi/{yonghuid}/{item.id}_{int(time.time())}_{uuid.uuid4().hex[:8]}.png'
|
|
image_file.seek(0)
|
|
full_url = upload_to_oss(image_file, oss_path)
|
|
if not full_url:
|
|
return Response({'code': 6, 'msg': '签名上传失败', 'data': None})
|
|
|
|
sign_shenfen = shenfen or item.shenfen
|
|
with transaction.atomic():
|
|
record = XcxXieyiQianshu.objects.create(
|
|
yonghuid=yonghuid,
|
|
xieyi=item,
|
|
xieyi_version=item.version,
|
|
shenfen=sign_shenfen,
|
|
signature_path=oss_path,
|
|
client_ip=_client_ip(request),
|
|
)
|
|
return Response({
|
|
'code': 0,
|
|
'msg': '签署成功',
|
|
'data': {
|
|
'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,
|
|
},
|
|
})
|