feat: 商家子客服 merchant_ops 模块与专用 API

This commit is contained in:
XingQue
2026-06-20 02:19:39 +08:00
parent e233ee4bea
commit 0cf5341d73
25 changed files with 2091 additions and 7 deletions

224
merchant_ops/models.py Normal file
View File

@@ -0,0 +1,224 @@
from decimal import Decimal
from django.db import models
from gvsdsdk.model_base import QModel
class MerchantStaffPermission(QModel):
"""权限字典(全局码表)"""
perm_code = models.CharField(max_length=64, unique=True, verbose_name='权限码')
perm_name = models.CharField(max_length=64, verbose_name='权限名称')
sort_order = models.IntegerField(default=0, verbose_name='排序')
status = models.IntegerField(default=1, verbose_name='1启用0停用')
CreateTime = models.DateTimeField(auto_now_add=True)
UpdateTime = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'merchant_staff_permission'
verbose_name = '商家客服权限字典'
class MerchantStaffRole(QModel):
"""商家内角色merchant_id 为空表示系统模板)"""
club_id = models.CharField(max_length=16, default='xq', verbose_name='Club')
merchant_id = models.CharField(
max_length=7, null=True, blank=True, db_index=True, verbose_name='商家ID'
)
role_code = models.CharField(max_length=32, verbose_name='角色码')
role_name = models.CharField(max_length=64, verbose_name='角色名')
is_system = models.BooleanField(default=False, verbose_name='系统预置')
sort_order = models.IntegerField(default=0)
status = models.IntegerField(default=1, verbose_name='1启用0停用')
CreateTime = models.DateTimeField(auto_now_add=True)
UpdateTime = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'merchant_staff_role'
verbose_name = '商家客服角色'
unique_together = [['merchant_id', 'role_code']]
class MerchantStaffRolePermission(QModel):
role = models.ForeignKey(
MerchantStaffRole, on_delete=models.CASCADE, related_name='role_permissions'
)
perm_code = models.CharField(max_length=64, db_index=True)
class Meta:
db_table = 'merchant_staff_role_permission'
unique_together = [['role', 'perm_code']]
class MerchantStaffInvite(QModel):
club_id = models.CharField(max_length=16, default='xq')
merchant_id = models.CharField(max_length=7, db_index=True, verbose_name='商家ID')
invite_code = models.CharField(max_length=32, unique=True, verbose_name='邀请码')
role = models.ForeignKey(
MerchantStaffRole, null=True, blank=True, on_delete=models.SET_NULL,
verbose_name='预绑角色'
)
status = models.IntegerField(default=0, verbose_name='0未用1已用2作废3过期')
created_by = models.CharField(max_length=7, verbose_name='创建人')
used_by = models.CharField(max_length=7, null=True, blank=True)
used_at = models.DateTimeField(null=True, blank=True)
expire_at = models.DateTimeField(null=True, blank=True)
CreateTime = models.DateTimeField(auto_now_add=True)
UpdateTime = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'merchant_staff_invite'
indexes = [
models.Index(fields=['merchant_id', 'status', '-CreateTime']),
]
class MerchantStaffMember(QModel):
club_id = models.CharField(max_length=16, default='xq')
merchant_id = models.CharField(max_length=7, db_index=True)
staff_user_id = models.CharField(max_length=7, db_index=True)
role = models.ForeignKey(MerchantStaffRole, on_delete=models.PROTECT)
status = models.IntegerField(default=1, verbose_name='1正常0停用2已移除')
display_name = models.CharField(max_length=64, blank=True, default='')
invited_by_code = models.ForeignKey(
MerchantStaffInvite, null=True, blank=True, on_delete=models.SET_NULL
)
joined_at = models.DateTimeField(auto_now_add=True)
revoked_at = models.DateTimeField(null=True, blank=True)
today_dispatch_count = models.IntegerField(default=0)
today_dispatch_amount = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00'))
CreateTime = models.DateTimeField(auto_now_add=True)
UpdateTime = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'merchant_staff_member'
unique_together = [['merchant_id', 'staff_user_id']]
indexes = [
models.Index(fields=['staff_user_id', 'status']),
]
class MerchantStaffMemberPermission(QModel):
member = models.ForeignKey(
MerchantStaffMember, on_delete=models.CASCADE, related_name='member_permissions'
)
perm_code = models.CharField(max_length=64)
granted = models.BooleanField(default=True, verbose_name='True授予False禁止')
class Meta:
db_table = 'merchant_staff_member_permission'
unique_together = [['member', 'perm_code']]
class MerchantStaffWallet(QModel):
merchant_id = models.CharField(max_length=7, db_index=True)
staff_user_id = models.CharField(max_length=7, db_index=True)
member = models.ForeignKey(
MerchantStaffMember, on_delete=models.CASCADE, related_name='wallets'
)
quota_total = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00'))
quota_used = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00'))
quota_frozen = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00'))
UpdateTime = models.DateTimeField(auto_now=True)
CreateTime = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'merchant_staff_wallet'
unique_together = [['merchant_id', 'staff_user_id']]
@property
def quota_available(self):
return self.quota_total - self.quota_used - self.quota_frozen
class MerchantStaffWalletLedger(QModel):
wallet = models.ForeignKey(MerchantStaffWallet, on_delete=models.CASCADE, related_name='ledgers')
merchant_id = models.CharField(max_length=7, db_index=True)
staff_user_id = models.CharField(max_length=7, db_index=True)
change_type = models.CharField(max_length=32)
amount = models.DecimalField(max_digits=12, decimal_places=2)
balance_after = models.DecimalField(max_digits=12, decimal_places=2)
related_order_id = models.CharField(max_length=32, null=True, blank=True)
operator_id = models.CharField(max_length=7)
remark = models.CharField(max_length=255, blank=True, default='')
CreateTime = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'merchant_staff_wallet_ledger'
indexes = [models.Index(fields=['merchant_id', '-CreateTime'])]
class MerchantStaffDailyStats(QModel):
club_id = models.CharField(max_length=16, default='xq')
merchant_id = models.CharField(max_length=7, db_index=True)
staff_user_id = models.CharField(max_length=7, db_index=True)
member_id = models.BigIntegerField(db_index=True)
stat_date = models.DateField(db_index=True)
year = models.PositiveSmallIntegerField()
month = models.PositiveSmallIntegerField()
day = models.PositiveSmallIntegerField()
dispatch_count = models.IntegerField(default=0)
dispatch_amount = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00'))
settle_count = models.IntegerField(default=0)
settle_amount = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00'))
refund_count = models.IntegerField(default=0)
refund_amount = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00'))
cancel_count = models.IntegerField(default=0)
penalty_count = models.IntegerField(default=0)
CreateTime = models.DateTimeField(auto_now_add=True)
UpdateTime = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'merchant_staff_ri_tongji'
unique_together = [['merchant_id', 'staff_user_id', 'stat_date']]
indexes = [
models.Index(fields=['merchant_id', 'stat_date', '-dispatch_count']),
]
class MerchantOrderDispatch(QModel):
"""订单派单归属(不修改 orders 表)"""
order_id = models.CharField(max_length=32, unique=True, db_index=True)
merchant_id = models.CharField(max_length=7, db_index=True)
dispatch_actor_type = models.CharField(max_length=16, verbose_name='MERCHANT/STAFF')
dispatch_actor_id = models.CharField(max_length=7)
staff_member_id = models.BigIntegerField(null=True, blank=True, db_index=True)
CreateTime = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'merchant_order_dispatch'
class MerchantStaffAuditLog(QModel):
merchant_id = models.CharField(max_length=7, db_index=True)
operator_user_id = models.CharField(max_length=7, db_index=True)
operator_type = models.CharField(max_length=16, verbose_name='MERCHANT_OWNER/STAFF')
staff_member_id = models.BigIntegerField(null=True, blank=True)
action = models.CharField(max_length=64, db_index=True)
resource_type = models.CharField(max_length=32, blank=True, default='')
resource_id = models.CharField(max_length=64, blank=True, default='')
value_before = models.JSONField(null=True, blank=True)
value_after = models.JSONField(null=True, blank=True)
amount = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
request_ip = models.CharField(max_length=64, blank=True, default='')
remark = models.CharField(max_length=500, blank=True, default='')
CreateTime = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'merchant_staff_audit_log'
indexes = [models.Index(fields=['merchant_id', '-CreateTime'])]
class MerchantDispatchTemplate(QModel):
merchant_id = models.CharField(max_length=7, db_index=True)
template_name = models.CharField(max_length=128)
template_type = models.CharField(max_length=16, default='EXPRESS')
payload_json = models.JSONField(default=dict)
created_by = models.CharField(max_length=7)
created_by_type = models.CharField(max_length=16, default='MERCHANT')
status = models.IntegerField(default=1)
use_count = models.IntegerField(default=0)
CreateTime = models.DateTimeField(auto_now_add=True)
UpdateTime = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'merchant_dispatch_template'