将文件夹命名从拼音改为英文,整理了一些代码,将导入语句迁移到了头部
This commit is contained in:
0
backend/_init_.py
Normal file
0
backend/_init_.py
Normal file
5
backend/apps.py
Normal file
5
backend/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class HoutaiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'houtai'
|
||||
296
backend/migrations/0001_initial.py
Normal file
296
backend/migrations/0001_initial.py
Normal file
@@ -0,0 +1,296 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-06 23:31
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="AbnormalUserLog",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"account_id",
|
||||
models.CharField(
|
||||
db_index=True, max_length=20, verbose_name="账号ID(phone)"
|
||||
),
|
||||
),
|
||||
(
|
||||
"ip_address",
|
||||
models.GenericIPAddressField(
|
||||
blank=True, null=True, verbose_name="IP地址"
|
||||
),
|
||||
),
|
||||
(
|
||||
"real_ip",
|
||||
models.GenericIPAddressField(
|
||||
blank=True, null=True, verbose_name="真实IP(穿透代理后)"
|
||||
),
|
||||
),
|
||||
(
|
||||
"x_forwarded_for",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
default="",
|
||||
max_length=255,
|
||||
verbose_name="X-Forwarded-For",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_agent",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
default="",
|
||||
max_length=500,
|
||||
verbose_name="User-Agent",
|
||||
),
|
||||
),
|
||||
(
|
||||
"request_method",
|
||||
models.CharField(
|
||||
blank=True, default="", max_length=10, verbose_name="请求方法"
|
||||
),
|
||||
),
|
||||
(
|
||||
"request_path",
|
||||
models.CharField(
|
||||
blank=True, default="", max_length=500, verbose_name="请求路径"
|
||||
),
|
||||
),
|
||||
(
|
||||
"request_params",
|
||||
models.TextField(
|
||||
blank=True, default="", verbose_name="请求参数(GET/POST)"
|
||||
),
|
||||
),
|
||||
(
|
||||
"request_body",
|
||||
models.TextField(
|
||||
blank=True, default="", verbose_name="请求体(原始)"
|
||||
),
|
||||
),
|
||||
(
|
||||
"request_headers",
|
||||
models.TextField(
|
||||
blank=True, default="", verbose_name="请求头(JSON格式)"
|
||||
),
|
||||
),
|
||||
(
|
||||
"attempt_type",
|
||||
models.CharField(
|
||||
blank=True, default="", max_length=50, verbose_name="攻击类型"
|
||||
),
|
||||
),
|
||||
(
|
||||
"detail",
|
||||
models.TextField(blank=True, default="", verbose_name="详细描述"),
|
||||
),
|
||||
(
|
||||
"create_time",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "异常用户日志",
|
||||
"verbose_name_plural": "异常用户日志",
|
||||
"db_table": "abnormal_user_log",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Permission",
|
||||
fields=[
|
||||
(
|
||||
"perm_id",
|
||||
models.AutoField(
|
||||
primary_key=True, serialize=False, verbose_name="权限ID"
|
||||
),
|
||||
),
|
||||
(
|
||||
"perm_code",
|
||||
models.CharField(
|
||||
db_index=True,
|
||||
max_length=100,
|
||||
unique=True,
|
||||
verbose_name="权限编码",
|
||||
),
|
||||
),
|
||||
(
|
||||
"perm_name",
|
||||
models.CharField(max_length=100, verbose_name="权限名称"),
|
||||
),
|
||||
(
|
||||
"description",
|
||||
models.CharField(
|
||||
blank=True, default="", max_length=200, verbose_name="权限描述"
|
||||
),
|
||||
),
|
||||
(
|
||||
"create_time",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
|
||||
),
|
||||
(
|
||||
"update_time",
|
||||
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "权限",
|
||||
"verbose_name_plural": "权限",
|
||||
"db_table": "permission",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Role",
|
||||
fields=[
|
||||
(
|
||||
"role_id",
|
||||
models.AutoField(
|
||||
primary_key=True, serialize=False, verbose_name="角色ID"
|
||||
),
|
||||
),
|
||||
(
|
||||
"role_code",
|
||||
models.CharField(
|
||||
db_index=True,
|
||||
max_length=50,
|
||||
unique=True,
|
||||
verbose_name="角色编码",
|
||||
),
|
||||
),
|
||||
("role_name", models.CharField(max_length=50, verbose_name="角色名称")),
|
||||
(
|
||||
"description",
|
||||
models.CharField(
|
||||
blank=True, default="", max_length=200, verbose_name="角色描述"
|
||||
),
|
||||
),
|
||||
(
|
||||
"create_time",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
|
||||
),
|
||||
(
|
||||
"update_time",
|
||||
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "角色",
|
||||
"verbose_name_plural": "角色",
|
||||
"db_table": "role",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="UserRole",
|
||||
fields=[
|
||||
("id", models.AutoField(primary_key=True, serialize=False)),
|
||||
(
|
||||
"account_id",
|
||||
models.CharField(
|
||||
db_index=True, max_length=11, verbose_name="账号ID"
|
||||
),
|
||||
),
|
||||
(
|
||||
"create_time",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
|
||||
),
|
||||
(
|
||||
"update_time",
|
||||
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
|
||||
),
|
||||
(
|
||||
"role",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="houtai.role",
|
||||
verbose_name="角色",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "用户角色关联",
|
||||
"verbose_name_plural": "用户角色关联",
|
||||
"db_table": "user_role",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="RolePermission",
|
||||
fields=[
|
||||
("id", models.AutoField(primary_key=True, serialize=False)),
|
||||
(
|
||||
"create_time",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
|
||||
),
|
||||
(
|
||||
"permission",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="houtai.permission",
|
||||
verbose_name="权限",
|
||||
),
|
||||
),
|
||||
(
|
||||
"role",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="houtai.role",
|
||||
verbose_name="角色",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "角色权限关联",
|
||||
"verbose_name_plural": "角色权限关联",
|
||||
"db_table": "role_permission",
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="role",
|
||||
index=models.Index(fields=["role_code"], name="role_role_co_b9f497_idx"),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="permission",
|
||||
index=models.Index(
|
||||
fields=["perm_code"], name="permission_perm_co_56cbe3_idx"
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="abnormaluserlog",
|
||||
index=models.Index(
|
||||
fields=["account_id"], name="abnormal_us_account_425899_idx"
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="abnormaluserlog",
|
||||
index=models.Index(
|
||||
fields=["ip_address"], name="abnormal_us_ip_addr_3d612b_idx"
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="abnormaluserlog",
|
||||
index=models.Index(
|
||||
fields=["create_time"], name="abnormal_us_create__ee16da_idx"
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="userrole",
|
||||
unique_together={("account_id", "role")},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="rolepermission",
|
||||
unique_together={("role", "permission")},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,339 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-09 14:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("houtai", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ZuzhangRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"yonghuid",
|
||||
models.CharField(
|
||||
db_index=True, max_length=7, verbose_name="组长用户ID"
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"yaoqing_guanshi_shu",
|
||||
models.IntegerField(default=0, verbose_name="邀请管事总数"),
|
||||
),
|
||||
(
|
||||
"shouru_zonge",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="收入总额(分佣)",
|
||||
),
|
||||
),
|
||||
(
|
||||
"fenyong_jine",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="分佣金额",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "组长每日统计",
|
||||
"db_table": "zuzhang_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "-yaoqing_guanshi_shu"],
|
||||
name="zuzhang_ri__riqi_87169e_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-shouru_zonge"],
|
||||
name="zuzhang_ri__riqi_1d7e51_idx",
|
||||
),
|
||||
],
|
||||
"unique_together": {("yonghuid", "riqi")},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ShangjiaRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"yonghuid",
|
||||
models.CharField(
|
||||
db_index=True, max_length=7, verbose_name="商家用户ID"
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"paifa_dingdan_shu",
|
||||
models.IntegerField(default=0, verbose_name="派发订单总数"),
|
||||
),
|
||||
(
|
||||
"paifa_jine",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="派发订单总金额",
|
||||
),
|
||||
),
|
||||
(
|
||||
"jiesuan_dingdan_shu",
|
||||
models.IntegerField(default=0, verbose_name="结算订单总数"),
|
||||
),
|
||||
(
|
||||
"jiesuan_jine",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="结算总金额",
|
||||
),
|
||||
),
|
||||
(
|
||||
"tuikuan_dingdan_shu",
|
||||
models.IntegerField(default=0, verbose_name="退款订单总数"),
|
||||
),
|
||||
(
|
||||
"tuikuan_jine",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="退款总金额",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "商家每日统计",
|
||||
"db_table": "shangjia_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "-paifa_jine"],
|
||||
name="shangjia_ri_riqi_d00f7f_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-jiesuan_jine"],
|
||||
name="shangjia_ri_riqi_25772a_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-paifa_dingdan_shu"],
|
||||
name="shangjia_ri_riqi_e05c54_idx",
|
||||
),
|
||||
],
|
||||
"unique_together": {("yonghuid", "riqi")},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="GuanshiXufeiRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"yonghuid",
|
||||
models.CharField(
|
||||
db_index=True, max_length=7, verbose_name="管事用户ID"
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"xufei_zongshu",
|
||||
models.IntegerField(default=0, verbose_name="今日续费总数"),
|
||||
),
|
||||
(
|
||||
"xufei_shouyi",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="续费收益金额",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "管事续费每日统计",
|
||||
"db_table": "guanshi_xufei_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "-xufei_zongshu"],
|
||||
name="guanshi_xuf_riqi_7c128f_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-xufei_shouyi"],
|
||||
name="guanshi_xuf_riqi_8f09b5_idx",
|
||||
),
|
||||
],
|
||||
"unique_together": {("yonghuid", "riqi")},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="GuanshiRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"yonghuid",
|
||||
models.CharField(
|
||||
db_index=True, max_length=7, verbose_name="管事用户ID"
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"yaoqing_dashou_shu",
|
||||
models.IntegerField(default=0, verbose_name="邀请打手总数"),
|
||||
),
|
||||
(
|
||||
"chongzhi_dashou_shu",
|
||||
models.IntegerField(default=0, verbose_name="充值打手数量"),
|
||||
),
|
||||
(
|
||||
"shouru_zonge",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="收入总额",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "管事每日统计",
|
||||
"db_table": "guanshi_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "-yaoqing_dashou_shu"],
|
||||
name="guanshi_ri__riqi_075e1a_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-chongzhi_dashou_shu"],
|
||||
name="guanshi_ri__riqi_78a79d_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-shouru_zonge"],
|
||||
name="guanshi_ri__riqi_334f52_idx",
|
||||
),
|
||||
],
|
||||
"unique_together": {("yonghuid", "riqi")},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="DashouRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"yonghuid",
|
||||
models.CharField(
|
||||
db_index=True, max_length=7, verbose_name="打手用户ID"
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"jiedan_zongliang",
|
||||
models.IntegerField(default=0, verbose_name="接单总量"),
|
||||
),
|
||||
(
|
||||
"chengjiao_zongliang",
|
||||
models.IntegerField(default=0, verbose_name="成交单总量"),
|
||||
),
|
||||
(
|
||||
"jiedan_zonge",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="接单总金额",
|
||||
),
|
||||
),
|
||||
(
|
||||
"chengjiao_zonge",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="成交总金额",
|
||||
),
|
||||
),
|
||||
(
|
||||
"tuikuan_liang",
|
||||
models.IntegerField(default=0, verbose_name="退款订单量"),
|
||||
),
|
||||
(
|
||||
"tuikuan_jine",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="退款总金额",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "打手每日统计",
|
||||
"db_table": "dashou_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "-jiedan_zongliang"],
|
||||
name="dashou_ri_t_riqi_95a785_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-chengjiao_zongliang"],
|
||||
name="dashou_ri_t_riqi_ad9c1a_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-jiedan_zonge"],
|
||||
name="dashou_ri_t_riqi_5da703_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-chengjiao_zonge"],
|
||||
name="dashou_ri_t_riqi_0a3156_idx",
|
||||
),
|
||||
],
|
||||
"unique_together": {("yonghuid", "riqi")},
|
||||
},
|
||||
),
|
||||
]
|
||||
54
backend/migrations/0003_tixianritongji.py
Normal file
54
backend/migrations/0003_tixianritongji.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-10 18:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("houtai", "0002_zuzhangritongji_shangjiaritongji_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="TixianRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"leixing",
|
||||
models.IntegerField(
|
||||
choices=[(1, "打手"), (2, "管事"), (3, "组长")],
|
||||
verbose_name="用户类型",
|
||||
),
|
||||
),
|
||||
(
|
||||
"total_amount",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="当天提现总额(元)",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "每日提现统计",
|
||||
"db_table": "tixian_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "leixing"], name="tixian_ri_t_riqi_c18bb2_idx"
|
||||
)
|
||||
],
|
||||
"unique_together": {("riqi", "leixing")},
|
||||
},
|
||||
),
|
||||
]
|
||||
0
backend/migrations/__init__.py
Normal file
0
backend/migrations/__init__.py
Normal file
0
backend/migrations/_init_.py
Normal file
0
backend/migrations/_init_.py
Normal file
270
backend/models.py
Normal file
270
backend/models.py
Normal file
@@ -0,0 +1,270 @@
|
||||
from django.db import models
|
||||
|
||||
# ==================== 1. 角色表 ====================
|
||||
class Role(models.Model):
|
||||
"""角色表"""
|
||||
role_id = models.AutoField(primary_key=True, verbose_name='角色ID')
|
||||
role_code = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='角色编码')
|
||||
role_name = models.CharField(max_length=50, verbose_name='角色名称')
|
||||
description = models.CharField(max_length=200, blank=True, default='', verbose_name='角色描述')
|
||||
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'role'
|
||||
verbose_name = '角色'
|
||||
verbose_name_plural = '角色'
|
||||
indexes = [
|
||||
models.Index(fields=['role_code']),
|
||||
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.role_name
|
||||
|
||||
|
||||
# ==================== 2. 权限表 ====================
|
||||
class Permission(models.Model):
|
||||
"""权限表"""
|
||||
perm_id = models.AutoField(primary_key=True, verbose_name='权限ID')
|
||||
perm_code = models.CharField(max_length=100, unique=True, db_index=True, verbose_name='权限编码')
|
||||
perm_name = models.CharField(max_length=100, verbose_name='权限名称')
|
||||
description = models.CharField(max_length=200, blank=True, default='', verbose_name='权限描述')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'permission'
|
||||
verbose_name = '权限'
|
||||
verbose_name_plural = '权限'
|
||||
indexes = [
|
||||
models.Index(fields=['perm_code']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.perm_name} ({self.perm_code})"
|
||||
|
||||
|
||||
# ==================== 3. 角色-权限关联表 ====================
|
||||
class RolePermission(models.Model):
|
||||
"""角色权限关联表"""
|
||||
id = models.AutoField(primary_key=True)
|
||||
role = models.ForeignKey(Role, on_delete=models.CASCADE, db_index=True, verbose_name='角色')
|
||||
permission = models.ForeignKey(Permission, on_delete=models.CASCADE, db_index=True, verbose_name='权限')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'role_permission'
|
||||
verbose_name = '角色权限关联'
|
||||
verbose_name_plural = '角色权限关联'
|
||||
unique_together = [['role', 'permission']] # 防止重复授权
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.role.role_name} -> {self.permission.perm_name}"
|
||||
|
||||
|
||||
# ==================== 4. 用户-角色关联表 ====================
|
||||
class UserRole(models.Model):
|
||||
"""用户角色关联表"""
|
||||
id = models.AutoField(primary_key=True)
|
||||
account_id = models.CharField(max_length=11, db_index=True, verbose_name='账号ID') # 对应 UserMain 的 phone 字段
|
||||
role = models.ForeignKey(Role, on_delete=models.CASCADE, db_index=True, verbose_name='角色')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'user_role'
|
||||
verbose_name = '用户角色关联'
|
||||
verbose_name_plural = '用户角色关联'
|
||||
unique_together = [['account_id', 'role']] # 同一用户同一角色只能有一条
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.account_id} -> {self.role.role_name}"
|
||||
|
||||
|
||||
class AbnormalUserLog(models.Model):
|
||||
"""记录恶意尝试越权操作的用户信息"""
|
||||
# 账号信息
|
||||
account_id = models.CharField(max_length=20, db_index=True, verbose_name='账号ID(phone)')
|
||||
|
||||
# 网络信息
|
||||
ip_address = models.GenericIPAddressField(null=True, blank=True, verbose_name='IP地址')
|
||||
real_ip = models.GenericIPAddressField(null=True, blank=True, verbose_name='真实IP(穿透代理后)')
|
||||
x_forwarded_for = models.CharField(max_length=255, blank=True, default='', verbose_name='X-Forwarded-For')
|
||||
user_agent = models.CharField(max_length=500, blank=True, default='', verbose_name='User-Agent')
|
||||
|
||||
# 地理位置信息(需借助第三方API,这里只存储请求时的IP,后续可离线解析)
|
||||
# 但我们可以存储更多请求细节
|
||||
request_method = models.CharField(max_length=10, blank=True, default='', verbose_name='请求方法')
|
||||
request_path = models.CharField(max_length=500, blank=True, default='', verbose_name='请求路径')
|
||||
request_params = models.TextField(blank=True, default='', verbose_name='请求参数(GET/POST)')
|
||||
request_body = models.TextField(blank=True, default='', verbose_name='请求体(原始)')
|
||||
request_headers = models.TextField(blank=True, default='', verbose_name='请求头(JSON格式)')
|
||||
|
||||
# 攻击特征
|
||||
attempt_type = models.CharField(max_length=50, blank=True, default='', verbose_name='攻击类型')
|
||||
detail = models.TextField(blank=True, default='', verbose_name='详细描述')
|
||||
|
||||
# 时间戳
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'abnormal_user_log'
|
||||
verbose_name = '异常用户日志'
|
||||
verbose_name_plural = verbose_name
|
||||
indexes = [
|
||||
models.Index(fields=['account_id']),
|
||||
models.Index(fields=['ip_address']),
|
||||
models.Index(fields=['create_time']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.account_id} @ {self.ip_address} - {self.create_time}"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ShangjiaRiTongji(models.Model):
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='商家用户ID')
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期') # 年月日
|
||||
|
||||
# 派发统计
|
||||
paifa_dingdan_shu = models.IntegerField(default=0, verbose_name='派发订单总数')
|
||||
paifa_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='派发订单总金额')
|
||||
|
||||
# 结算统计(成交)
|
||||
jiesuan_dingdan_shu = models.IntegerField(default=0, verbose_name='结算订单总数')
|
||||
jiesuan_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='结算总金额')
|
||||
|
||||
# 退款统计(可选)
|
||||
tuikuan_dingdan_shu = models.IntegerField(default=0, verbose_name='退款订单总数')
|
||||
tuikuan_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='退款总金额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'shangjia_ri_tongji'
|
||||
verbose_name = '商家每日统计'
|
||||
unique_together = [['yonghuid', 'riqi']] # 每人每天一条记录
|
||||
indexes = [
|
||||
models.Index(fields=['riqi', '-paifa_jine']),
|
||||
models.Index(fields=['riqi', '-jiesuan_jine']),
|
||||
models.Index(fields=['riqi', '-paifa_dingdan_shu']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class DashouRiTongji(models.Model):
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='打手用户ID')
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期') # 年月日
|
||||
|
||||
# 接单统计
|
||||
jiedan_zongliang = models.IntegerField(default=0, verbose_name='接单总量')
|
||||
chengjiao_zongliang = models.IntegerField(default=0, verbose_name='成交单总量')
|
||||
|
||||
# 金额统计
|
||||
jiedan_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='接单总金额')
|
||||
chengjiao_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='成交总金额')
|
||||
|
||||
# 退款统计
|
||||
tuikuan_liang = models.IntegerField(default=0, verbose_name='退款订单量')
|
||||
tuikuan_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='退款总金额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'dashou_ri_tongji'
|
||||
verbose_name = '打手每日统计'
|
||||
unique_together = [['yonghuid', 'riqi']]
|
||||
indexes = [
|
||||
models.Index(fields=['riqi', '-jiedan_zongliang']),
|
||||
models.Index(fields=['riqi', '-chengjiao_zongliang']),
|
||||
models.Index(fields=['riqi', '-jiedan_zonge']),
|
||||
models.Index(fields=['riqi', '-chengjiao_zonge']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
class ZuzhangRiTongji(models.Model):
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='组长用户ID')
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期') # 年月日
|
||||
|
||||
yaoqing_guanshi_shu = models.IntegerField(default=0, verbose_name='邀请管事总数')
|
||||
shouru_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='收入总额(分佣)')
|
||||
|
||||
# 可选明细
|
||||
fenyong_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='分佣金额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'zuzhang_ri_tongji'
|
||||
verbose_name = '组长每日统计'
|
||||
unique_together = [['yonghuid', 'riqi']]
|
||||
indexes = [
|
||||
models.Index(fields=['riqi', '-yaoqing_guanshi_shu']),
|
||||
models.Index(fields=['riqi', '-shouru_zonge']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
class GuanshiRiTongji(models.Model):
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='管事用户ID')
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期') # 年月日
|
||||
|
||||
yaoqing_dashou_shu = models.IntegerField(default=0, verbose_name='邀请打手总数')
|
||||
chongzhi_dashou_shu = models.IntegerField(default=0, verbose_name='充值打手数量')
|
||||
shouru_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='收入总额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'guanshi_ri_tongji'
|
||||
verbose_name = '管事每日统计'
|
||||
unique_together = [['yonghuid', 'riqi']]
|
||||
indexes = [
|
||||
models.Index(fields=['riqi', '-yaoqing_dashou_shu']),
|
||||
models.Index(fields=['riqi', '-chongzhi_dashou_shu']),
|
||||
models.Index(fields=['riqi', '-shouru_zonge']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
class GuanshiXufeiRiTongji(models.Model):
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='管事用户ID')
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期') # 年月日
|
||||
|
||||
xufei_zongshu = models.IntegerField(default=0, verbose_name='今日续费总数') # 续费订单数(或续费打手数)
|
||||
xufei_shouyi = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='续费收益金额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'guanshi_xufei_ri_tongji'
|
||||
verbose_name = '管事续费每日统计'
|
||||
unique_together = [['yonghuid', 'riqi']]
|
||||
indexes = [
|
||||
models.Index(fields=['riqi', '-xufei_zongshu']),
|
||||
models.Index(fields=['riqi', '-xufei_shouyi']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
# models.py 中添加
|
||||
|
||||
class TixianRiTongji(models.Model):
|
||||
"""
|
||||
每日提现统计表(按身份统计当天提现总额)
|
||||
"""
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期')
|
||||
leixing = models.IntegerField(
|
||||
choices=[(1, '打手'), (2, '管事'), (3, '组长'),
|
||||
(4, '考核官'), (5, '打手押金'), (6, '商家余额')],
|
||||
verbose_name='用户类型'
|
||||
)
|
||||
total_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='当天提现总额(元)')
|
||||
|
||||
class Meta:
|
||||
db_table = 'tixian_ri_tongji'
|
||||
verbose_name = '每日提现统计'
|
||||
unique_together = [['riqi', 'leixing']] # 每天每个身份一条记录
|
||||
indexes = [models.Index(fields=['riqi', 'leixing'])]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.riqi} - {self.get_leixing_display()} 提现总额: {self.total_amount}"
|
||||
39
backend/serializers.py
Normal file
39
backend/serializers.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# serializers.py
|
||||
|
||||
#小程序弹窗序列化器
|
||||
from rest_framework import serializers
|
||||
from config.models import PopupPage, PopupConfig, PopupImage
|
||||
|
||||
class PopupImageSerializer(serializers.ModelSerializer):
|
||||
"""图片序列化器(输出用)"""
|
||||
class Meta:
|
||||
model = PopupImage
|
||||
fields = ['id', 'image_url', 'text', 'sort_order']
|
||||
|
||||
|
||||
class PopupConfigSerializer(serializers.ModelSerializer):
|
||||
"""弹窗配置序列化器(输出用)"""
|
||||
images = PopupImageSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PopupConfig
|
||||
fields = [
|
||||
'id', 'popup_id', 'title', 'description',
|
||||
'strategy_type', 'max_count', 'reset_interval',
|
||||
'duration', 'force_even_muted', 'sort_order',
|
||||
'is_active', 'start_time', 'end_time',
|
||||
'created_at', 'updated_at', 'images'
|
||||
]
|
||||
|
||||
|
||||
class PopupPageSerializer(serializers.ModelSerializer):
|
||||
"""页面序列化器(输出用)"""
|
||||
popups = PopupConfigSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PopupPage
|
||||
fields = [
|
||||
'id', 'page_key', 'name', 'description',
|
||||
'is_active', 'ignore_user_mute',
|
||||
'created_at', 'updated_at', 'popups'
|
||||
]
|
||||
137
backend/urls.py
Normal file
137
backend/urls.py
Normal file
@@ -0,0 +1,137 @@
|
||||
from django.urls import path
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from .view import GetClubConfigView, GetCrossOrderListView, PartnerGetOrderDataView,PartnerRefundNotifyView,\
|
||||
CrossPlatformRefundView, GetCrossOrderDetailView, CrossPlatformForceCompleteView, CrossPlatformTransferHallView,\
|
||||
PartnerTransferHallNotifyView, GetSettingsView, UpdateSettingsView, GetMappingsView, UpdateMappingsView,\
|
||||
PartnerUpdateSelfConfigView, GetPartnerClubsView, GetStatsView, PunishDashouView, PartnerPunishNotifyView,\
|
||||
GetClubConfigForPreSettlementView, PreSettlementActionView, GetPreSettlementDataView, GetRolePermissionView,\
|
||||
ModifyRolePermissionView, AddRoleView, GetAdminRolesView, GetAdminUserListView, AddAdminUserView,\
|
||||
ModifyAdminUserView, KefuGetDashouListView, KefuGetDashouDetailView, KefuUpdateDashouView, KefuGetShangjiaListView,\
|
||||
KefuGetShangjiaDetailView, KefuUpdateShangjiaView, GetProductBaseDataView, GetProductListView, SaveProductOrderView,\
|
||||
AddProductView, DeleteProductView, GetProductDetailView, UpdateProductView, UpdateMemberView, GetMemberListView,\
|
||||
AddMemberView, GetGuanliListView, GetGuanliDetailView, UpdateGuanliView, GetWithdrawSettingsView,\
|
||||
UpdateWithdrawSettingsView, GetZuzhangListView, GetZuzhangDetailView, UpdateZuzhangView, GetProductTypeZoneView,\
|
||||
ModifyProductTypeZoneView, GetRateView, ModifyRateView, PopupNoticeListAPIView, PopupNoticeModifyAPIView,ShopListView, ShopModifyView, ShopPublicTypeAndAuditView, ShopListForProductView, ShopProductModifyView,\
|
||||
ShopProductTypeMappingView, ProductListView, ProductModifyView, KefuChatPermissionsView,\
|
||||
PartnerFineNotifyView,FineApplyView, FaKuanTongJiView, FaKuanLieBiaoView, FaKuanChuLiView, FaKuanChuangJianView,\
|
||||
HqbkxxView,BkxgView,CaiwuView,CwhybkhqView, HybkjtsjView, SzxxView, CwddhqlxView, CwhqjtddsjView,\
|
||||
CwqtczhqView, KhpzhqView, ChzsgcView,KhgglView, ShgxgsjView, ZxkfghdsView, KptxwztjbView
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
path('kpthq', GetClubConfigView.as_view(), name='跨平台订单页面获取俱乐部数据'),
|
||||
path('hqddlb', GetCrossOrderListView.as_view(), name='获取跨平台订单'),
|
||||
path('kpthqtp',csrf_exempt(PartnerGetOrderDataView.as_view()), name='跨平台接受对方服务器获取订单数据'),
|
||||
# 对方平台退款通知接口
|
||||
path('dfddtk', csrf_exempt(PartnerRefundNotifyView.as_view()), name='跨平台接受对方服务器更新订单状态'),
|
||||
path('kptk', CrossPlatformRefundView.as_view(), name='跨平台订单退款'),
|
||||
path('hqkptddxq', GetCrossOrderDetailView.as_view(), name='获取跨平台订单数据详情'),
|
||||
path('kpqzjd', CrossPlatformForceCompleteView.as_view(), name='跨平台订单结算'),
|
||||
path('kpzydt', CrossPlatformTransferHallView.as_view(), name='跨平台转移大厅'),
|
||||
path('kpttzzydt', csrf_exempt(PartnerTransferHallNotifyView.as_view()), name='跨平台接受对方转移大厅通知'),
|
||||
path('hqkptsz', GetSettingsView.as_view(), name='获取俱乐部数据信息'),
|
||||
path('xgjlbsj', UpdateSettingsView.as_view(), name='更新俱乐部数据信息'),
|
||||
path('hqqtjlbsj', GetMappingsView.as_view(), name='获取俱乐部订单类型映射'),
|
||||
path('xgdfddys', UpdateMappingsView.as_view(), name='工薪俱乐部订单类型映射'),
|
||||
path('xgzjsjcs', csrf_exempt(PartnerUpdateSelfConfigView.as_view()), name='partner_update_self_config'),
|
||||
path('kpthqpt', GetPartnerClubsView.as_view(), name='前端获取俱乐部数据展示派发数据'),
|
||||
path('hqpfsj', GetStatsView.as_view(), name='前端派发统计表获取'),
|
||||
path('kpcf', PunishDashouView.as_view(), name='跨平台处罚打手'),
|
||||
path('kptcfds', PartnerPunishNotifyView.as_view(), name='接受跨平台对方平台处罚申请'),
|
||||
path('hqkttjsyjs', GetClubConfigForPreSettlementView.as_view(), name='预处理获取俱乐部数据'),
|
||||
path('hqyjssj', GetPreSettlementDataView.as_view(), name='获取预处理数据'),
|
||||
path('yjsjjhdk', PreSettlementActionView.as_view(), name='操作预处理'),
|
||||
|
||||
path('gljshq', GetRolePermissionView.as_view(), name='获取就角色信息'),
|
||||
path('sctjjsnr', ModifyRolePermissionView.as_view(), name='修改角色数据'),
|
||||
path('tjjs', AddRoleView.as_view(), name='添加角色'),
|
||||
|
||||
path('hqyhjsgl', GetAdminRolesView.as_view(), name='管理用户页面获取用户角色'),
|
||||
path('hqjsyhsj', GetAdminUserListView.as_view(), name='管理用户页面获取用户数据'),
|
||||
path('xghtyhsj', ModifyAdminUserView.as_view(), name='管理用户页面修改用户数据'),
|
||||
path('tjyhjs', AddAdminUserView.as_view(), name='管理用户页面添加用户'),
|
||||
path('kefuhqdslb', KefuGetDashouListView.as_view(), name='客服获取打手数据'),
|
||||
path('kefuhqdsxq', KefuGetDashouDetailView.as_view(), name='客服获取打手具体信息'),
|
||||
path('kefuxgds', KefuUpdateDashouView.as_view(), name='客服修改打手数据'),
|
||||
path('hqsjgllb', KefuGetShangjiaListView.as_view(), name='后台获取商家列表'),
|
||||
path('hthqsjxq', KefuGetShangjiaDetailView.as_view(), name='后台获取商家详情'),
|
||||
path('xgsjxx', KefuUpdateShangjiaView.as_view(), name='后台修改商家数据'),
|
||||
|
||||
path('htsphqjcsx', GetProductBaseDataView.as_view(), name='后台获取商品类型专区'),
|
||||
path('hthqsplb', GetProductListView.as_view(), name='后台获取商品数据'),
|
||||
path('htsppaixu', SaveProductOrderView.as_view(), name='后台修改商品类型排序'),
|
||||
path('htfbsp', AddProductView.as_view(), name='后台添加商品'),
|
||||
path('htscsp', DeleteProductView.as_view(), name='后台删除商品'),
|
||||
path('htsphqxq', GetProductDetailView.as_view(), name='后台获取商品详情'),
|
||||
path('bcspxg', UpdateProductView.as_view(), name='后台更新商品数据'),
|
||||
|
||||
path('hthqhylb', GetMemberListView.as_view(), name='后台获取会员列表'),
|
||||
path('htxghyxx', UpdateMemberView.as_view(), name='后台更新会员信息'),
|
||||
path('httjhy', AddMemberView.as_view(), name='后台添加会员'),
|
||||
path('hthqgslb', GetGuanliListView.as_view(), name='后台获取管事列表'),
|
||||
|
||||
path('hthqgsxq', GetGuanliDetailView.as_view(), name='后台获取管事详情信息'),
|
||||
path('htxggsxx', UpdateGuanliView.as_view(), name='后台更新管事数据'),
|
||||
path('hthqtxpz', GetWithdrawSettingsView.as_view(), name='后台获取提现配置'),
|
||||
path('htxgtxsz', UpdateWithdrawSettingsView.as_view(), name='后台修改提现配置'),
|
||||
path('hthqzzlb', GetZuzhangListView.as_view(), name='后台获取组长列表'),
|
||||
path('hthqzzxq', GetZuzhangDetailView.as_view(), name='后台获取组长详情页面'),
|
||||
path('htxgzzxx', UpdateZuzhangView.as_view(), name='后台修改组长信息'),
|
||||
|
||||
|
||||
path('hthqsplxzq', GetProductTypeZoneView.as_view(), name='后台获取商品类型专区'),
|
||||
path('htxgsplx', ModifyProductTypeZoneView.as_view(), name='后台修改添加商品类型专区'),
|
||||
path('hthqfhpz', GetRateView.as_view(), name='后台获取费率'),
|
||||
path('htxgfl', ModifyRateView.as_view(), name='后台修改费率'),
|
||||
path('hthqtcxx', PopupNoticeListAPIView.as_view(), name='后台获取弹窗配置'),
|
||||
path('htxgtcxx', PopupNoticeModifyAPIView.as_view(), name='后台更新创建弹窗配置'),
|
||||
|
||||
path('hqdplbsj', ShopListView.as_view(), name='获取店铺列表数据'),
|
||||
path('xgdpxx', ShopModifyView.as_view(), name='修改或添加店铺数据'),
|
||||
|
||||
|
||||
path('htdphqsplx', ShopPublicTypeAndAuditView.as_view(), name='后台获取店铺公共商品类型,默认审核配置'),
|
||||
path('htdphqdpys', ShopListForProductView.as_view(), name='后台获取商品店铺'),
|
||||
path('htdphqyssplx', ShopProductTypeMappingView.as_view(), name='后台获取商品店铺类型'),
|
||||
path('htxgdpsplx', ShopProductModifyView.as_view(), name='后台修改店铺商品类型和默认审核模式'),
|
||||
|
||||
path('hqhtdpsplx', ProductListView.as_view(), name='后台获取店铺商品数据'),
|
||||
path('htxgdpspsj', ProductModifyView.as_view(), name='后台修改店铺商品数据'),
|
||||
|
||||
path('kfhqltqx', KefuChatPermissionsView.as_view(), name='后台获取客服聊天配置'),
|
||||
|
||||
path('kffkdssq', FineApplyView.as_view(), name='跨平台处罚罚款申请接口'),
|
||||
path('kffkdssq_notify', PartnerFineNotifyView.as_view(), name='跨屏要对方平台处罚通知接口'),
|
||||
|
||||
|
||||
path('htglyhqcfsltj', FaKuanTongJiView.as_view(),name='后台罚单统计图片'), # 罚款统计
|
||||
path('hthqfklb', FaKuanLieBiaoView.as_view(),name='后台罚单列表'), # 罚款列表
|
||||
path('glyclfk', FaKuanChuLiView.as_view(),name='后台罚单操作'), # 处理罚款
|
||||
path('htfksc', FaKuanChuangJianView.as_view(),name='后台生成罚单'), # 创建罚款
|
||||
|
||||
path('hqbkxx', HqbkxxView.as_view(), name='获取板块数据'), # 获取板块数据
|
||||
path('bkxg', BkxgView.as_view(), name='板块增删改查'), # 板块增删改
|
||||
path('caiwu', CaiwuView.as_view(), name='财务页面基础数据'),
|
||||
|
||||
path('cwhybkhq', CwhybkhqView.as_view(), name='财务会员模块'),
|
||||
path('hybkjtsj', HybkjtsjView.as_view(), name='财务会员统计'),
|
||||
path('szxx', SzxxView.as_view(), name='详细收支记录'),
|
||||
|
||||
path('cwddhqlx', CwddhqlxView.as_view(), name='后台财务获取订单类型'),
|
||||
path('cwhqjtddsj', CwhqjtddsjView.as_view(), name='后台财务获取订单数据'),
|
||||
path('cwqtczhq', CwqtczhqView.as_view(), name='财务获取其他充值统计'),
|
||||
|
||||
path('khpzhq', KhpzhqView.as_view(), name='后台获取称号配置'),
|
||||
path('chzsgc', ChzsgcView.as_view(), name='后台对称号增删改查'),
|
||||
|
||||
path('khggl', KhgglView.as_view(), name='获取考核管信息'),
|
||||
path('shgxgsj', ShgxgsjView.as_view(), name='修改考核管数据'),
|
||||
|
||||
path('zxkfghds', ZxkfghdsView.as_view(), name='链接换打手或重新下单'),
|
||||
|
||||
path('kptxwztjb', csrf_exempt(KptxwztjbView.as_view()), name='跨平台脚本询问订单状态'),
|
||||
|
||||
|
||||
|
||||
]
|
||||
422
backend/utils.py
Normal file
422
backend/utils.py
Normal file
@@ -0,0 +1,422 @@
|
||||
# houtai/utils.py
|
||||
import logging
|
||||
import json
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from rest_framework.response import Response
|
||||
from .models import AbnormalUserLog, UserRole, RolePermission, Permission, DashouRiTongji, ShangjiaRiTongji, \
|
||||
GuanshiXufeiRiTongji, GuanshiRiTongji, ZuzhangRiTongji, TixianRiTongji
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_real_ip(request):
|
||||
"""获取真实IP(考虑代理)"""
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
ip = x_forwarded_for.split(',')[0].strip()
|
||||
return ip, x_forwarded_for
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip, ''
|
||||
|
||||
|
||||
def _record_abnormal(account_id, request, attempt_type, detail=''):
|
||||
"""记录异常操作(仅用于越权等攻击行为)"""
|
||||
try:
|
||||
real_ip, xff = _get_real_ip(request)
|
||||
headers = {k: v for k, v in request.META.items() if k.startswith('HTTP_')}
|
||||
headers_json = json.dumps(headers, ensure_ascii=False)[:2000]
|
||||
|
||||
request_params = json.dumps(request.query_params.dict(), ensure_ascii=False) if request.query_params else ''
|
||||
request_body = request.body.decode('utf-8', errors='ignore')[:5000]
|
||||
|
||||
AbnormalUserLog.objects.create(
|
||||
account_id=account_id,
|
||||
ip_address=real_ip,
|
||||
real_ip=real_ip,
|
||||
x_forwarded_for=xff,
|
||||
user_agent=request.META.get('HTTP_USER_AGENT', ''),
|
||||
request_method=request.method,
|
||||
request_path=request.path,
|
||||
request_params=request_params,
|
||||
request_body=request_body,
|
||||
request_headers=headers_json,
|
||||
attempt_type=attempt_type,
|
||||
detail=detail,
|
||||
)
|
||||
logger.warning(f"记录异常用户: {account_id} IP:{real_ip} 类型:{attempt_type}")
|
||||
except Exception as e:
|
||||
logger.error(f"记录异常日志失败: {e}")
|
||||
|
||||
|
||||
def verify_kefu_permission(request, username_from_frontend=None):
|
||||
"""
|
||||
验证客服身份,并返回该用户的所有权限列表(基于新RBAC表)
|
||||
只有前端传递的username与当前登录用户不匹配时,才记录异常并警告前端。
|
||||
|
||||
参数:
|
||||
request: DRF request 对象
|
||||
username_from_frontend: 前端传递的账号(用于防越权)
|
||||
|
||||
返回:
|
||||
(kefu_obj, permissions_list) # 验证通过
|
||||
(None, Response) # 验证失败,直接返回Response
|
||||
"""
|
||||
current_user = request.user
|
||||
|
||||
# ---------- 1. 必须是客服 ----------
|
||||
if current_user.user_type != 'kefu':
|
||||
return None, Response({'code': 403, 'msg': '身份错误,非客服账号'}, status=403)
|
||||
|
||||
# ---------- 2. 客服扩展表存在且状态正常 ----------
|
||||
try:
|
||||
kefu = current_user.kefu_profile
|
||||
if kefu.zhuangtai != 1:
|
||||
return None, Response({'code': 403, 'msg': '客服账号已被禁用'}, status=403)
|
||||
except ObjectDoesNotExist:
|
||||
return None, Response({'code': 403, 'msg': '客服账号不存在'}, status=403)
|
||||
|
||||
# ---------- 3. 越权检测(只记录前端篡改username的情况) ----------
|
||||
if username_from_frontend and current_user.phone != username_from_frontend:
|
||||
detail = f'前端username: {username_from_frontend},实际登录账号: {current_user.phone}'
|
||||
_record_abnormal(current_user.phone, request, '越权尝试篡改username', detail=detail)
|
||||
return None, Response({
|
||||
'code': 403,
|
||||
'msg': '身份错误,无权操作其他账号,相关操作已记录'
|
||||
}, status=403)
|
||||
|
||||
# ---------- 4. 获取用户的所有权限(通过角色) ----------
|
||||
# 步骤1:获取用户的所有角色ID
|
||||
role_ids = UserRole.objects.filter(account_id=current_user.phone).values_list('role_id', flat=True)
|
||||
# 步骤2:通过角色ID获取权限编码
|
||||
permissions = list(Permission.objects.filter(
|
||||
rolepermission__role_id__in=role_ids
|
||||
).values_list('perm_code', flat=True).distinct())
|
||||
|
||||
if not permissions:
|
||||
logger.warning(f"用户 {current_user.phone} 没有任何权限,请检查角色分配")
|
||||
|
||||
return kefu, permissions
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def update_dashou_daily_by_action(yonghuid, amount, action):
|
||||
"""
|
||||
根据行为类型更新打手每日统计(接单、成交、退款)
|
||||
|
||||
参数:
|
||||
yonghuid: 打手用户ID
|
||||
amount: 本单涉及的金额(Decimal)
|
||||
action: 1 = 接单, 2 = 成交(结算), 3 = 退款
|
||||
"""
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount <= 0:
|
||||
return # 金额非正不统计
|
||||
|
||||
today = date.today()
|
||||
|
||||
with transaction.atomic():
|
||||
stat, created = DashouRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'jiedan_zongliang': 0,
|
||||
'jiedan_zonge': Decimal('0.00'),
|
||||
'chengjiao_zongliang': 0,
|
||||
'chengjiao_zonge': Decimal('0.00'),
|
||||
'tuikuan_liang': 0,
|
||||
'tuikuan_jine': Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
update_fields = {}
|
||||
if action == 1: # 接单
|
||||
update_fields['jiedan_zongliang'] = F('jiedan_zongliang') + 1
|
||||
update_fields['jiedan_zonge'] = F('jiedan_zonge') + amount
|
||||
elif action == 2: # 成交
|
||||
update_fields['chengjiao_zongliang'] = F('chengjiao_zongliang') + 1
|
||||
update_fields['chengjiao_zonge'] = F('chengjiao_zonge') + amount
|
||||
elif action == 3: # 退款
|
||||
update_fields['tuikuan_liang'] = F('tuikuan_liang') + 1
|
||||
update_fields['tuikuan_jine'] = F('tuikuan_jine') + amount
|
||||
else:
|
||||
return # 无效行为
|
||||
|
||||
if update_fields:
|
||||
DashouRiTongji.objects.filter(id=stat.id).update(**update_fields)
|
||||
|
||||
|
||||
def update_shangjia_daily(yonghuid, amount, action):
|
||||
"""
|
||||
根据操作行为更新商家每日统计(派发、结算、退款)
|
||||
|
||||
参数:
|
||||
yonghuid: 商家用户ID
|
||||
amount: 当前订单涉及的金额(Decimal)
|
||||
action: 操作类型,1 = 派发,2 = 结算(成交),3 = 退款
|
||||
|
||||
说明:
|
||||
- 金额必须大于0,否则不执行任何操作
|
||||
- 使用 select_for_update + F() 表达式保证原子性和并发安全
|
||||
- 每天每个商家只会有一条统计记录,不存在则自动创建
|
||||
"""
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount <= 0:
|
||||
return # 金额非正,不统计
|
||||
|
||||
today = date.today()
|
||||
|
||||
with transaction.atomic():
|
||||
# 获取或创建当天统计记录,同时加锁防止并发
|
||||
stat, created = ShangjiaRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'paifa_dingdan_shu': 0,
|
||||
'paifa_jine': Decimal('0.00'),
|
||||
'jiesuan_dingdan_shu': 0,
|
||||
'jiesuan_jine': Decimal('0.00'),
|
||||
'tuikuan_dingdan_shu': 0,
|
||||
'tuikuan_jine': Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
# 构建本次更新的字段,使用 F 表达式进行原子累加
|
||||
update_fields = {}
|
||||
if action == 1: # 派发
|
||||
update_fields['paifa_dingdan_shu'] = F('paifa_dingdan_shu') + 1
|
||||
update_fields['paifa_jine'] = F('paifa_jine') + amount
|
||||
elif action == 2: # 结算(成交)
|
||||
update_fields['jiesuan_dingdan_shu'] = F('jiesuan_dingdan_shu') + 1
|
||||
update_fields['jiesuan_jine'] = F('jiesuan_jine') + amount
|
||||
elif action == 3: # 退款
|
||||
update_fields['tuikuan_dingdan_shu'] = F('tuikuan_dingdan_shu') + 1
|
||||
update_fields['tuikuan_jine'] = F('tuikuan_jine') + amount
|
||||
else:
|
||||
# 无效的行为类型,直接返回
|
||||
return
|
||||
|
||||
# 执行原子更新
|
||||
ShangjiaRiTongji.objects.filter(id=stat.id).update(**update_fields)
|
||||
|
||||
|
||||
|
||||
def update_guanshi_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
|
||||
"""
|
||||
根据操作行为更新管事每日统计(邀请打手、充值会员、订单/押金分红)
|
||||
action: 1 = 邀请打手, 2 = 充值会员, 3 = 商家订单分红, 4 = 押金分红
|
||||
"""
|
||||
today = date.today()
|
||||
|
||||
with transaction.atomic():
|
||||
stat, created = GuanshiRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'yaoqing_dashou_shu': 0,
|
||||
'chongzhi_dashou_shu': 0,
|
||||
'shouru_zonge': Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
if action == 1:
|
||||
# 邀请打手:无论新创建还是已存在,直接 +1
|
||||
if created:
|
||||
stat.yaoqing_dashou_shu = 1
|
||||
stat.save()
|
||||
else:
|
||||
GuanshiRiTongji.objects.filter(id=stat.id).update(
|
||||
yaoqing_dashou_shu=F('yaoqing_dashou_shu') + 1
|
||||
)
|
||||
|
||||
elif action == 2:
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount <= 0:
|
||||
return
|
||||
if created:
|
||||
stat.chongzhi_dashou_shu = 1
|
||||
stat.shouru_zonge = amount
|
||||
stat.save()
|
||||
else:
|
||||
GuanshiRiTongji.objects.filter(id=stat.id).update(
|
||||
chongzhi_dashou_shu=F('chongzhi_dashou_shu') + 1,
|
||||
shouru_zonge=F('shouru_zonge') + amount
|
||||
)
|
||||
|
||||
elif action in (3, 4):
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount <= 0:
|
||||
return
|
||||
if created:
|
||||
stat.shouru_zonge = amount
|
||||
stat.save()
|
||||
else:
|
||||
GuanshiRiTongji.objects.filter(id=stat.id).update(
|
||||
shouru_zonge=F('shouru_zonge') + amount
|
||||
)
|
||||
|
||||
'''def update_guanshi_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
|
||||
"""
|
||||
根据操作行为更新管事每日统计(邀请打手、充值会员)
|
||||
|
||||
参数:
|
||||
yonghuid: 管事用户ID
|
||||
action: 操作类型
|
||||
1 = 邀请打手(次数自动+1,无需传其他参数)
|
||||
2 = 充值会员(需要传 amount 参数)
|
||||
amount: 充值金额(Decimal),仅在 action=2 时有效
|
||||
|
||||
说明:
|
||||
- 每次调用只代表一次操作,不会批量处理。
|
||||
- 使用 select_for_update + F() 保证原子性和并发安全。
|
||||
"""
|
||||
today = date.today()
|
||||
|
||||
# 邀请打手:次数+1,金额不变
|
||||
if action == 1:
|
||||
count_field = 'yaoqing_dashou_shu'
|
||||
amount = Decimal('0.00')
|
||||
|
||||
# 充值会员:次数+1,金额累加
|
||||
elif action == 2:
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount <= 0:
|
||||
return
|
||||
count_field = 'chongzhi_dashou_shu'
|
||||
|
||||
else:
|
||||
return # 无效行为,直接返回
|
||||
|
||||
with transaction.atomic():
|
||||
stat, created = GuanshiRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'yaoqing_dashou_shu': 0,
|
||||
'chongzhi_dashou_shu': 0,
|
||||
'shouru_zonge': Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
if created:
|
||||
# 新记录直接赋初值
|
||||
if action == 1:
|
||||
stat.yaoqing_dashou_shu = 1
|
||||
else:
|
||||
# 已存在记录,使用 F 表达式原子累加次数
|
||||
update_fields = {count_field: F(count_field) + 1}
|
||||
|
||||
if action == 2:
|
||||
update_fields['shouru_zonge'] = F('shouru_zonge') + amount
|
||||
|
||||
GuanshiRiTongji.objects.filter(id=stat.id).update(**update_fields)'''
|
||||
|
||||
def update_guanshi_xufei_daily(yonghuid, xufei_jine=Decimal('0.00')):
|
||||
"""
|
||||
管事续费统计(单独表)
|
||||
用法: update_guanshi_xufei_daily('300001', Decimal('30'))
|
||||
"""
|
||||
today = date.today()
|
||||
with transaction.atomic():
|
||||
stat, created = GuanshiXufeiRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'xufei_zongshu': 1,
|
||||
'xufei_shouyi': xufei_jine,
|
||||
}
|
||||
)
|
||||
if not created:
|
||||
GuanshiXufeiRiTongji.objects.filter(id=stat.id).update(
|
||||
xufei_zongshu=F('xufei_zongshu') + 1,
|
||||
xufei_shouyi=F('xufei_shouyi') + xufei_jine
|
||||
)
|
||||
|
||||
|
||||
def update_zuzhang_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
|
||||
"""
|
||||
根据操作行为更新组长每日统计(邀请管事、分佣收入)
|
||||
|
||||
参数:
|
||||
yonghuid: 组长用户ID
|
||||
action: 操作类型
|
||||
1 = 邀请管事(次数+1,无金额)
|
||||
2 = 分佣收入(累加金额)
|
||||
amount: 分佣金额(Decimal),仅在 action=2 时有效
|
||||
"""
|
||||
today = date.today()
|
||||
|
||||
with transaction.atomic():
|
||||
stat, created = ZuzhangRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'yaoqing_guanshi_shu': 0,
|
||||
'shouru_zonge': Decimal('0.00'),
|
||||
'fenyong_jine': Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
if created:
|
||||
# 新记录直接设置初始值
|
||||
if action == 1:
|
||||
stat.yaoqing_guanshi_shu = 1
|
||||
elif action == 2:
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
stat.shouru_zonge = amount
|
||||
stat.fenyong_jine = amount
|
||||
stat.save()
|
||||
else:
|
||||
# 已存在记录,使用 F 表达式原子累加
|
||||
if action == 1:
|
||||
ZuzhangRiTongji.objects.filter(id=stat.id).update(
|
||||
yaoqing_guanshi_shu=F('yaoqing_guanshi_shu') + 1
|
||||
)
|
||||
elif action == 2:
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount > 0:
|
||||
ZuzhangRiTongji.objects.filter(id=stat.id).update(
|
||||
shouru_zonge=F('shouru_zonge') + amount,
|
||||
fenyong_jine=F('fenyong_jine') + amount
|
||||
)
|
||||
|
||||
|
||||
|
||||
def update_tixian_daily_stat(leixing, amount):
|
||||
"""
|
||||
更新每日提现统计(平台维度)
|
||||
leixing: 1=打手, 2=管事, 3=组长
|
||||
amount: 本次提现金额(申请金额,含手续费)
|
||||
"""
|
||||
today = date.today()
|
||||
with transaction.atomic():
|
||||
stat, created = TixianRiTongji.objects.select_for_update().get_or_create(
|
||||
riqi=today,
|
||||
leixing=leixing,
|
||||
defaults={'total_amount': amount}
|
||||
)
|
||||
if not created:
|
||||
TixianRiTongji.objects.filter(id=stat.id).update(
|
||||
total_amount=F('total_amount') + amount
|
||||
)
|
||||
12064
backend/view.py
Normal file
12064
backend/view.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user