f
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -29,3 +29,6 @@ documents/*
|
||||
!documents/.gitkeep
|
||||
|
||||
deploy/script/js
|
||||
|
||||
# 授权文档
|
||||
app/main/api/data/authorization_docs/
|
||||
|
||||
@@ -59,6 +59,9 @@ type (
|
||||
PayTimeEnd string `form:"pay_time_end,optional"` // 支付时间结束
|
||||
RefundTimeStart string `form:"refund_time_start,optional"` // 退款时间开始
|
||||
RefundTimeEnd string `form:"refund_time_end,optional"` // 退款时间结束
|
||||
QueryUserName string `form:"query_user_name,optional"` // 被查询人姓名
|
||||
QueryUserIdCard string `form:"query_user_id_card,optional"` // 被查询人身份证号
|
||||
QueryUserMobile string `form:"query_user_mobile,optional"` // 被查询人手机号
|
||||
}
|
||||
// 列表响应
|
||||
AdminGetOrderListResp {
|
||||
|
||||
@@ -86,7 +86,7 @@ Tianyuanapi:
|
||||
BaseURL: "https://api.tianyuanapi.com"
|
||||
Timeout: 60
|
||||
Authorization:
|
||||
FileBaseURL: "https://www.quannengcha.com/api/v1/auth-docs" # 授权书文件访问基础URL
|
||||
FileBaseURL: "https://www.zhenaicha.com/api/v1/auth-docs" # 授权书文件访问基础URL
|
||||
Promotion:
|
||||
PromotionDomain: "http://localhost:8888" # 推广域名(用于生成短链)
|
||||
OfficialDomain: "http://localhost:5678" # 正式站点域名(短链重定向的目标域名)
|
||||
|
||||
@@ -2,6 +2,7 @@ package admin_order
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"sync"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/globalkey"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
@@ -74,6 +76,112 @@ func (l *AdminGetOrderListLogic) AdminGetOrderList(req *types.AdminGetOrderListR
|
||||
builder = builder.Where("refund_time <= ?", req.RefundTimeEnd)
|
||||
}
|
||||
|
||||
// 根据被查询人信息筛选订单(姓名、身份证、手机号)
|
||||
// 由于数据是加密存储的,需要先加密查询条件,然后通过子查询筛选
|
||||
// 支持单独使用任一要素查询,也支持组合查询(AND 关系)
|
||||
if req.QueryUserName != "" || req.QueryUserIdCard != "" || req.QueryUserMobile != "" {
|
||||
// 获取加密密钥
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
key, decodeErr := hex.DecodeString(secretKey)
|
||||
if decodeErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminGetOrderList, 解析加密密钥失败 err: %v", decodeErr)
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
queryUserRecordBuilder := l.svcCtx.QueryUserRecordModel.SelectBuilder().
|
||||
Where("del_state = ?", globalkey.DelStateNo)
|
||||
|
||||
// 加密并添加姓名筛选条件
|
||||
if req.QueryUserName != "" {
|
||||
encryptedName, err := crypto.AesEcbEncrypt([]byte(req.QueryUserName), key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminGetOrderList, 加密姓名失败 err: %v", err)
|
||||
}
|
||||
logx.Infof("AdminGetOrderList, 姓名加密: 明文=%s, 密文=%s", req.QueryUserName, encryptedName)
|
||||
queryUserRecordBuilder = queryUserRecordBuilder.Where("name = ?", encryptedName)
|
||||
}
|
||||
|
||||
// 加密并添加身份证筛选条件
|
||||
if req.QueryUserIdCard != "" {
|
||||
encryptedIdCard, err := crypto.EncryptIDCard(req.QueryUserIdCard, key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminGetOrderList, 加密身份证号失败 err: %v", err)
|
||||
}
|
||||
logx.Infof("AdminGetOrderList, 身份证号加密: 明文=%s, 密文=%s", req.QueryUserIdCard, encryptedIdCard)
|
||||
queryUserRecordBuilder = queryUserRecordBuilder.Where("id_card = ?", encryptedIdCard)
|
||||
}
|
||||
|
||||
// 加密并添加手机号筛选条件
|
||||
if req.QueryUserMobile != "" {
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.QueryUserMobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminGetOrderList, 加密手机号失败 err: %v", err)
|
||||
}
|
||||
logx.Infof("AdminGetOrderList, 手机号加密: 明文=%s, 密文=%s", req.QueryUserMobile, encryptedMobile)
|
||||
queryUserRecordBuilder = queryUserRecordBuilder.Where("mobile = ?", encryptedMobile)
|
||||
}
|
||||
|
||||
// 先查询符合条件的记录(包含 order_id 和 query_no)
|
||||
queryUserRecords, err := l.svcCtx.QueryUserRecordModel.FindAll(l.ctx, queryUserRecordBuilder, "")
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 查询用户记录失败 err: %v", err)
|
||||
}
|
||||
logx.Infof("AdminGetOrderList, 查询条件: 姓名=%s, 身份证=%s, 手机号=%s, 查询到 %d 条匹配的查询用户记录",
|
||||
req.QueryUserName, req.QueryUserIdCard, req.QueryUserMobile, len(queryUserRecords))
|
||||
|
||||
if len(queryUserRecords) == 0 {
|
||||
// 如果没有匹配的记录,返回空结果
|
||||
logx.Infof("AdminGetOrderList, 未找到匹配的查询用户记录,返回空结果")
|
||||
return &types.AdminGetOrderListResp{
|
||||
Total: 0,
|
||||
Items: []types.OrderListItem{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 提取 order_id 和 query_no 列表
|
||||
orderIds := make([]string, 0, len(queryUserRecords))
|
||||
queryNos := make([]string, 0, len(queryUserRecords))
|
||||
|
||||
for _, record := range queryUserRecords {
|
||||
// 优先使用 order_id(如果已创建订单)
|
||||
if record.OrderId.Valid && record.OrderId.String != "" {
|
||||
orderIds = append(orderIds, record.OrderId.String)
|
||||
}
|
||||
// 同时收集 query_no(用于关联订单,query_no 与 order.order_no 一致)
|
||||
if record.QueryNo != "" {
|
||||
queryNos = append(queryNos, record.QueryNo)
|
||||
}
|
||||
}
|
||||
|
||||
logx.Infof("AdminGetOrderList, 提取到 %d 个 order_id, %d 个 query_no", len(orderIds), len(queryNos))
|
||||
|
||||
// 构建订单筛选条件
|
||||
// 使用 OR 条件:通过 order.id 或 order.order_no 匹配
|
||||
if len(orderIds) > 0 && len(queryNos) > 0 {
|
||||
// 同时有 order_id 和 query_no,使用 OR 条件
|
||||
builder = builder.Where(squirrel.Or{
|
||||
squirrel.Eq{"id": orderIds},
|
||||
squirrel.Eq{"order_no": queryNos},
|
||||
})
|
||||
logx.Infof("AdminGetOrderList, 使用 OR 条件: order_id IN (%d个), order_no IN (%d个)", len(orderIds), len(queryNos))
|
||||
} else if len(orderIds) > 0 {
|
||||
// 只有 order_id
|
||||
builder = builder.Where(squirrel.Eq{"id": orderIds})
|
||||
logx.Infof("AdminGetOrderList, 使用 order_id 条件: %d 个订单ID", len(orderIds))
|
||||
} else if len(queryNos) > 0 {
|
||||
// 只有 query_no
|
||||
builder = builder.Where(squirrel.Eq{"order_no": queryNos})
|
||||
logx.Infof("AdminGetOrderList, 使用 query_no 条件: %d 个查询单号", len(queryNos))
|
||||
} else {
|
||||
// 如果没有有效的订单ID或查询单号,返回空结果
|
||||
logx.Infof("AdminGetOrderList, 未找到有效的订单ID或查询单号,返回空结果")
|
||||
return &types.AdminGetOrderListResp{
|
||||
Total: 0,
|
||||
Items: []types.OrderListItem{},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 并发获取总数和列表
|
||||
var total int64
|
||||
var orders []*model.Order
|
||||
|
||||
@@ -26,6 +26,6 @@ func NewGetAppVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get
|
||||
func (l *GetAppVersionLogic) GetAppVersion() (resp *types.GetAppVersionResp, err error) {
|
||||
return &types.GetAppVersionResp{
|
||||
Version: "1.0.0",
|
||||
WgtUrl: "https://www.quannengcha.com/app_version/qnc_1.0.0.wgt",
|
||||
WgtUrl: "https://www.zhenaicha.com/app_version/qnc_1.0.0.wgt",
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -99,6 +99,24 @@ func (l *AlipayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, not
|
||||
return nil
|
||||
}
|
||||
|
||||
// 更新 query_user_record 表的 platform_order_id
|
||||
queryUserRecords, findRecordErr := l.svcCtx.QueryUserRecordModel.FindAll(l.ctx,
|
||||
l.svcCtx.QueryUserRecordModel.SelectBuilder().
|
||||
Where("query_no = ?", notification.OutTradeNo).
|
||||
Where("del_state = ?", 0).
|
||||
Limit(1), "")
|
||||
if findRecordErr == nil && len(queryUserRecords) > 0 {
|
||||
record := queryUserRecords[0]
|
||||
record.PlatformOrderId = lzUtils.StringToNullString(notification.TradeNo)
|
||||
record.Version = record.Version + 1
|
||||
if updateRecordErr := l.svcCtx.QueryUserRecordModel.UpdateWithVersion(l.ctx, nil, record); updateRecordErr != nil {
|
||||
logx.Errorf("支付宝支付回调,更新查询用户记录失败: %+v", updateRecordErr)
|
||||
// 更新失败不影响主流程,只记录日志
|
||||
} else {
|
||||
logx.Infof("支付宝支付回调,更新查询用户记录成功,query_no: %s, platform_order_id: %s", notification.OutTradeNo, notification.TradeNo)
|
||||
}
|
||||
}
|
||||
|
||||
if order.Status == "paid" {
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
logx.Errorf("异步任务调度失败: %v", asyncErr)
|
||||
|
||||
@@ -3,19 +3,23 @@ package pay
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/ctxdata"
|
||||
"qnc-server/common/globalkey"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redis/go-redis/v9"
|
||||
@@ -241,6 +245,16 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 获取代理链接失败: %+v", findAgentLinkErr)
|
||||
}
|
||||
amount = agentLinkModel.SetPrice
|
||||
|
||||
// 检查被查询人身份证在72小时内的已支付订单次数
|
||||
// 如果是代理渠道订单,需要检查该身份证在72小时内已支付的订单是否超过2次
|
||||
logx.Infof("生成订单, 检测到代理渠道订单,开始检查订单限制, AgentIdentifier: %s", data.AgentIdentifier)
|
||||
checkErr := l.checkIdCardPaidOrdersIn72Hours(data.Params)
|
||||
if checkErr != nil {
|
||||
logx.Errorf("生成订单, 订单限制检查失败: %v", checkErr)
|
||||
return nil, checkErr
|
||||
}
|
||||
logx.Infof("生成订单, 订单限制检查通过,允许创建订单")
|
||||
} else {
|
||||
amount = product.SellPrice
|
||||
}
|
||||
@@ -264,6 +278,24 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses
|
||||
}
|
||||
orderID := order.Id
|
||||
|
||||
// 更新 query_user_record 表的 order_id(通过 query_no 匹配)
|
||||
queryUserRecords, findRecordErr := l.svcCtx.QueryUserRecordModel.FindAll(l.ctx,
|
||||
l.svcCtx.QueryUserRecordModel.SelectBuilder().
|
||||
Where("query_no = ?", outTradeNo).
|
||||
Where("del_state = ?", 0).
|
||||
Limit(1), "")
|
||||
if findRecordErr == nil && len(queryUserRecords) > 0 {
|
||||
record := queryUserRecords[0]
|
||||
record.OrderId = lzUtils.StringToNullString(orderID)
|
||||
record.Version = record.Version + 1
|
||||
if updateRecordErr := l.svcCtx.QueryUserRecordModel.UpdateWithVersion(l.ctx, session, record); updateRecordErr != nil {
|
||||
logx.Errorf("更新查询用户记录 order_id 失败: %+v", updateRecordErr)
|
||||
// 更新失败不影响主流程,只记录日志
|
||||
} else {
|
||||
logx.Infof("更新查询用户记录成功,query_no: %s, order_id: %s", outTradeNo, orderID)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是代理推广订单,创建完整的代理订单记录
|
||||
if data.AgentIdentifier != "" && agentLinkModel != nil {
|
||||
// 获取代理信息
|
||||
@@ -473,3 +505,158 @@ func (l *PaymentLogic) getConfigFloat(configKey string) (float64, error) {
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// checkIdCardPaidOrdersIn72Hours 检查被查询人身份证在72小时内的已支付订单次数
|
||||
// 如果72小时内已支付订单大于2次,则返回错误
|
||||
// encryptedParams: 加密的参数字符串(data.Params),需要解密后获取身份证号
|
||||
func (l *PaymentLogic) checkIdCardPaidOrdersIn72Hours(encryptedParams string) error {
|
||||
logx.Infof("检查订单限制, 开始检查代理渠道订单限制, encryptedParams长度: %d", len(encryptedParams))
|
||||
|
||||
// 1. 解密参数获取身份证号
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
key, decodeErr := hex.DecodeString(secretKey)
|
||||
if decodeErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "检查订单限制, 解析加密密钥失败 err: %v", decodeErr)
|
||||
}
|
||||
|
||||
// 解密 data.Params(加密的 JSON 字符串)
|
||||
decryptData, aesDecryptErr := crypto.AesDecrypt(encryptedParams, key)
|
||||
if aesDecryptErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "检查订单限制, 解密参数失败 err: %v", aesDecryptErr)
|
||||
}
|
||||
|
||||
// 解析解密后的 JSON 获取参数
|
||||
var params map[string]interface{}
|
||||
if unmarshalErr := json.Unmarshal(decryptData, ¶ms); unmarshalErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "检查订单限制, 解析参数失败 err: %v, decryptData: %s", unmarshalErr, string(decryptData))
|
||||
}
|
||||
|
||||
idCard, ok := params["id_card"].(string)
|
||||
if !ok || idCard == "" {
|
||||
// 如果没有身份证号,跳过检查(可能是其他类型的查询)
|
||||
logx.Infof("检查订单限制, 未找到身份证号,跳过检查")
|
||||
return nil
|
||||
}
|
||||
|
||||
logx.Infof("检查订单限制, 被查询人身份证号: %s", idCard)
|
||||
|
||||
// 2. 加密身份证号用于查询
|
||||
logx.Infof("检查订单限制, 开始加密身份证号: %s", idCard)
|
||||
encryptedIdCard, encryptErr := crypto.EncryptIDCard(idCard, key)
|
||||
if encryptErr != nil {
|
||||
logx.Errorf("检查订单限制, 加密身份证号失败: %v", encryptErr)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "检查订单限制, 加密身份证号失败 err: %v", encryptErr)
|
||||
}
|
||||
logx.Infof("检查订单限制, 身份证号加密成功, encryptedIdCard长度: %d", len(encryptedIdCard))
|
||||
|
||||
// 3. 查询该身份证对应的所有查询记录(agent_identifier IS NOT NULL)
|
||||
// 统计该身份证的记录数,然后通过order_id关联订单表查询已支付的订单
|
||||
encryptedIdCardPreview := encryptedIdCard
|
||||
if len(encryptedIdCard) > 20 {
|
||||
encryptedIdCardPreview = encryptedIdCard[:20] + "..."
|
||||
}
|
||||
logx.Infof("检查订单限制, 开始查询数据库")
|
||||
logx.Infof("检查订单限制, 查询条件: id_card=%s, del_state=0, agent_identifier IS NOT NULL, agent_identifier != ''", encryptedIdCardPreview)
|
||||
logx.Infof("检查订单限制, 注意:不要求order_id必须存在,先查询所有记录")
|
||||
queryUserRecords, findRecordErr := l.svcCtx.QueryUserRecordModel.FindAll(l.ctx,
|
||||
l.svcCtx.QueryUserRecordModel.SelectBuilder().
|
||||
Where("id_card = ?", encryptedIdCard).
|
||||
Where("del_state = ?", globalkey.DelStateNo).
|
||||
Where("agent_identifier IS NOT NULL").
|
||||
Where("agent_identifier != ''"),
|
||||
"")
|
||||
if findRecordErr != nil {
|
||||
if errors.Is(findRecordErr, model.ErrNotFound) {
|
||||
logx.Infof("检查订单限制, 查询结果: 未找到记录 (ErrNotFound)")
|
||||
} else {
|
||||
logx.Errorf("检查订单限制, 查询数据库失败: %v", findRecordErr)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "检查订单限制, 查询用户记录失败 err: %v", findRecordErr)
|
||||
}
|
||||
} else {
|
||||
logx.Infof("检查订单限制, 查询数据库成功")
|
||||
}
|
||||
|
||||
logx.Infof("检查订单限制, 查询到 %d 条该身份证的查询记录(agent_identifier IS NOT NULL)", len(queryUserRecords))
|
||||
|
||||
// 统计记录详情
|
||||
if len(queryUserRecords) > 0 {
|
||||
logx.Infof("检查订单限制, ========== 记录详情 ==========")
|
||||
for i, record := range queryUserRecords {
|
||||
orderId := ""
|
||||
hasOrderId := false
|
||||
if record.OrderId.Valid && record.OrderId.String != "" {
|
||||
orderId = record.OrderId.String
|
||||
hasOrderId = true
|
||||
}
|
||||
agentId := ""
|
||||
if record.AgentIdentifier.Valid {
|
||||
agentId = record.AgentIdentifier.String
|
||||
}
|
||||
logx.Infof("检查订单限制, 记录[%d/%d]: order_id=%s (有order_id: %v), agent_identifier=%s, query_no=%s, create_time=%s, product=%s",
|
||||
i+1, len(queryUserRecords), orderId, hasOrderId, agentId, record.QueryNo, record.CreateTime.Format("2006-01-02 15:04:05"), record.Product)
|
||||
}
|
||||
logx.Infof("检查订单限制, ========== 记录详情结束 ==========")
|
||||
}
|
||||
|
||||
if len(queryUserRecords) == 0 {
|
||||
// 没有历史订单记录,可以继续
|
||||
logx.Infof("检查订单限制, 身份证 %s 无历史订单记录,允许支付", idCard)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 4. 提取所有订单ID(去重),只提取有order_id的记录
|
||||
logx.Infof("检查订单限制, 开始提取订单ID,总记录数: %d", len(queryUserRecords))
|
||||
orderIdSet := make(map[string]bool)
|
||||
orderIds := make([]string, 0)
|
||||
recordsWithOrderId := 0
|
||||
for i, record := range queryUserRecords {
|
||||
if record.OrderId.Valid && record.OrderId.String != "" {
|
||||
recordsWithOrderId++
|
||||
orderId := record.OrderId.String
|
||||
if !orderIdSet[orderId] {
|
||||
orderIdSet[orderId] = true
|
||||
orderIds = append(orderIds, orderId)
|
||||
logx.Infof("检查订单限制, 记录[%d]有order_id: %s", i+1, orderId)
|
||||
} else {
|
||||
logx.Infof("检查订单限制, 记录[%d]order_id重复: %s (已存在)", i+1, orderId)
|
||||
}
|
||||
} else {
|
||||
logx.Infof("检查订单限制, 记录[%d]无order_id,跳过", i+1)
|
||||
}
|
||||
}
|
||||
|
||||
logx.Infof("检查订单限制, 有order_id的记录数: %d, 提取到 %d 个唯一订单ID: %v", recordsWithOrderId, len(orderIds), orderIds)
|
||||
|
||||
if len(orderIds) == 0 {
|
||||
logx.Infof("检查订单限制, 身份证 %s 无有效订单ID,允许支付", idCard)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 5. 查询72小时内已支付的订单数量(只统计已支付状态的订单)
|
||||
// 计算72小时前的时间
|
||||
seventyTwoHoursAgo := time.Now().Add(-72 * time.Hour)
|
||||
logx.Infof("检查订单限制, 查询时间范围: %s 至今", seventyTwoHoursAgo.Format("2006-01-02 15:04:05"))
|
||||
logx.Infof("检查订单限制, 查询条件: status='paid', pay_time IS NOT NULL, pay_time >= %s", seventyTwoHoursAgo.Format("2006-01-02 15:04:05"))
|
||||
|
||||
// 只统计已支付状态的订单(status='paid')
|
||||
paidOrderCount, countErr := l.svcCtx.OrderModel.FindCount(l.ctx,
|
||||
l.svcCtx.OrderModel.SelectBuilder().
|
||||
Where(squirrel.Eq{"id": orderIds}).
|
||||
Where("status = ?", model.OrderStatusPaid). // 只查询已支付状态的订单
|
||||
Where("pay_time IS NOT NULL"). // 必须有支付时间
|
||||
Where("pay_time >= ?", seventyTwoHoursAgo). // 72小时内
|
||||
Where("del_state = ?", globalkey.DelStateNo),
|
||||
"id")
|
||||
if countErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "检查订单限制, 查询已支付订单数量失败 err: %v", countErr)
|
||||
}
|
||||
|
||||
logx.Infof("检查订单限制, 身份证 %s 在72小时内已支付的订单数量: %d (限制: 已支付订单数量>2次则拒绝)", idCard, paidOrderCount)
|
||||
|
||||
// 6. 如果72小时内已支付订单数量大于2次,则拒绝支付
|
||||
if paidOrderCount > 2 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("该身份证在72小时内已支付订单超过2次,无法进行代理渠道的报告查询支付"), "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -97,6 +97,24 @@ func (l *WechatPayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter,
|
||||
return nil
|
||||
}
|
||||
|
||||
// 更新 query_user_record 表的 platform_order_id
|
||||
queryUserRecords, findRecordErr := l.svcCtx.QueryUserRecordModel.FindAll(l.ctx,
|
||||
l.svcCtx.QueryUserRecordModel.SelectBuilder().
|
||||
Where("query_no = ?", *notification.OutTradeNo).
|
||||
Where("del_state = ?", 0).
|
||||
Limit(1), "")
|
||||
if findRecordErr == nil && len(queryUserRecords) > 0 {
|
||||
record := queryUserRecords[0]
|
||||
record.PlatformOrderId = lzUtils.StringToNullString(*notification.TransactionId)
|
||||
record.Version = record.Version + 1
|
||||
if updateRecordErr := l.svcCtx.QueryUserRecordModel.UpdateWithVersion(l.ctx, nil, record); updateRecordErr != nil {
|
||||
logx.Errorf("微信支付回调,更新查询用户记录失败: %+v", updateRecordErr)
|
||||
// 更新失败不影响主流程,只记录日志
|
||||
} else {
|
||||
logx.Infof("微信支付回调,更新查询用户记录成功,query_no: %s, platform_order_id: %s", *notification.OutTradeNo, *notification.TransactionId)
|
||||
}
|
||||
}
|
||||
|
||||
if order.Status == "paid" {
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
logx.Errorf("异步任务调度失败: %v", asyncErr)
|
||||
|
||||
@@ -7,12 +7,15 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"qnc-server/app/main/api/internal/service"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/ctxdata"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
|
||||
@@ -107,6 +110,11 @@ func (l *QueryServiceLogic) ProcessMarriageLogic(req *types.QueryServiceReq) (*t
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
// 插入业务操作记录
|
||||
if insertErr := l.InsertQueryUserRecord(params, "marriage", cacheNo, userID); insertErr != nil {
|
||||
logx.Errorf("插入查询用户记录失败: %v", insertErr)
|
||||
// 记录失败不影响主流程,只记录日志
|
||||
}
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %v", err)
|
||||
@@ -167,7 +175,11 @@ func (l *QueryServiceLogic) ProcessHomeServiceLogic(req *types.QueryServiceReq)
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
// 插入业务操作记录
|
||||
if insertErr := l.InsertQueryUserRecord(params, "homeservice", cacheNo, userID); insertErr != nil {
|
||||
logx.Errorf("插入查询用户记录失败: %v", insertErr)
|
||||
// 记录失败不影响主流程,只记录日志
|
||||
}
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
@@ -228,7 +240,11 @@ func (l *QueryServiceLogic) ProcessRiskAssessmentLogic(req *types.QueryServiceRe
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
// 插入业务操作记录
|
||||
if insertErr := l.InsertQueryUserRecord(params, "riskassessment", cacheNo, userID); insertErr != nil {
|
||||
logx.Errorf("插入查询用户记录失败: %v", insertErr)
|
||||
// 记录失败不影响主流程,只记录日志
|
||||
}
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
@@ -288,7 +304,11 @@ func (l *QueryServiceLogic) ProcessCompanyInfoLogic(req *types.QueryServiceReq)
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
// 插入业务操作记录
|
||||
if insertErr := l.InsertQueryUserRecord(params, "companyinfo", cacheNo, userID); insertErr != nil {
|
||||
logx.Errorf("插入查询用户记录失败: %v", insertErr)
|
||||
// 记录失败不影响主流程,只记录日志
|
||||
}
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
@@ -349,7 +369,11 @@ func (l *QueryServiceLogic) ProcessRentalInfoLogic(req *types.QueryServiceReq) (
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
// 插入业务操作记录
|
||||
if insertErr := l.InsertQueryUserRecord(params, "rentalinfo", cacheNo, userID); insertErr != nil {
|
||||
logx.Errorf("插入查询用户记录失败: %v", insertErr)
|
||||
// 记录失败不影响主流程,只记录日志
|
||||
}
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
@@ -410,7 +434,11 @@ func (l *QueryServiceLogic) ProcessPreLoanBackgroundCheckLogic(req *types.QueryS
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
// 插入业务操作记录
|
||||
if insertErr := l.InsertQueryUserRecord(params, "preloanbackgroundcheck", cacheNo, userID); insertErr != nil {
|
||||
logx.Errorf("插入查询用户记录失败: %v", insertErr)
|
||||
// 记录失败不影响主流程,只记录日志
|
||||
}
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
@@ -470,7 +498,11 @@ func (l *QueryServiceLogic) ProcessBackgroundCheckLogic(req *types.QueryServiceR
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
// 插入业务操作记录
|
||||
if insertErr := l.InsertQueryUserRecord(params, "backgroundcheck", cacheNo, userID); insertErr != nil {
|
||||
logx.Errorf("插入查询用户记录失败: %v", insertErr)
|
||||
// 记录失败不影响主流程,只记录日志
|
||||
}
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
@@ -528,7 +560,11 @@ func (l *QueryServiceLogic) ProcessPersonalDataLogic(req *types.QueryServiceReq)
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
// 插入业务操作记录
|
||||
if insertErr := l.InsertQueryUserRecord(params, "personalData", cacheNo, userID); insertErr != nil {
|
||||
logx.Errorf("插入查询用户记录失败: %v", insertErr)
|
||||
// 记录失败不影响主流程,只记录日志
|
||||
}
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
@@ -586,7 +622,11 @@ func (l *QueryServiceLogic) ProcessConsumerFinanceReportLogic(req *types.QuerySe
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
// 插入业务操作记录
|
||||
if insertErr := l.InsertQueryUserRecord(params, "consumerFinanceReport", cacheNo, userID); insertErr != nil {
|
||||
logx.Errorf("插入查询用户记录失败: %v", insertErr)
|
||||
// 记录失败不影响主流程,只记录日志
|
||||
}
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
@@ -734,3 +774,61 @@ func (l *QueryServiceLogic) GetOrCreateUser() (string, error) {
|
||||
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
// InsertQueryUserRecord 插入查询用户记录到 query_user_record 表
|
||||
// 业务场景:用户提交查询时,记录用户输入的姓名、身份证、手机号等信息
|
||||
func (l *QueryServiceLogic) InsertQueryUserRecord(params map[string]interface{}, product string, queryNo string, userID string) error {
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
key, decodeErr := hex.DecodeString(secretKey)
|
||||
if decodeErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "插入查询用户记录, 获取AES秘钥失败: %+v", decodeErr)
|
||||
}
|
||||
|
||||
// 获取姓名、身份证、手机号
|
||||
name, _ := params["name"].(string)
|
||||
idCard, _ := params["id_card"].(string)
|
||||
mobile, _ := params["mobile"].(string)
|
||||
|
||||
// 加密敏感字段
|
||||
encryptedName, err := crypto.AesEcbEncrypt([]byte(name), key)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "插入查询用户记录, 加密姓名失败: %+v", err)
|
||||
}
|
||||
|
||||
encryptedIdCard, err := crypto.EncryptIDCard(idCard, key)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "插入查询用户记录, 加密身份证号失败: %+v", err)
|
||||
}
|
||||
|
||||
encryptedMobile, err := crypto.EncryptMobile(mobile, secretKey)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "插入查询用户记录, 加密手机号失败: %+v", err)
|
||||
}
|
||||
|
||||
// 获取代理标识
|
||||
agentIdentifier, _ := l.ctx.Value("agentIdentifier").(string)
|
||||
|
||||
// 创建记录
|
||||
record := &model.QueryUserRecord{
|
||||
Id: uuid.NewString(), // 生成 UUID
|
||||
UserId: userID, // user_id 是 UUID 字符串
|
||||
Name: encryptedName,
|
||||
IdCard: encryptedIdCard,
|
||||
Mobile: encryptedMobile,
|
||||
Product: product,
|
||||
QueryNo: queryNo,
|
||||
OrderId: lzUtils.StringToNullString(""), // 初始为 NULL,创建订单后更新
|
||||
PlatformOrderId: lzUtils.StringToNullString(""), // 初始为空,支付成功后更新
|
||||
AgentIdentifier: lzUtils.StringToNullString(agentIdentifier),
|
||||
DelState: 0,
|
||||
Version: 0,
|
||||
}
|
||||
|
||||
// 插入数据库
|
||||
_, err = l.svcCtx.QueryUserRecordModel.Insert(l.ctx, nil, record)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "插入查询用户记录失败: %+v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hibiken/asynq"
|
||||
|
||||
@@ -38,6 +38,7 @@ type ServiceContext struct {
|
||||
OrderModel model.OrderModel
|
||||
OrderRefundModel model.OrderRefundModel
|
||||
QueryModel model.QueryModel
|
||||
QueryUserRecordModel model.QueryUserRecordModel
|
||||
QueryCleanupLogModel model.QueryCleanupLogModel
|
||||
QueryCleanupDetailModel model.QueryCleanupDetailModel
|
||||
QueryCleanupConfigModel model.QueryCleanupConfigModel
|
||||
@@ -118,6 +119,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
// ============================== 订单相关模型 ==============================
|
||||
orderModel := model.NewOrderModel(db, cacheConf)
|
||||
queryModel := model.NewQueryModel(db, cacheConf)
|
||||
queryUserRecordModel := model.NewQueryUserRecordModel(db, cacheConf)
|
||||
orderRefundModel := model.NewOrderRefundModel(db, cacheConf)
|
||||
queryCleanupLogModel := model.NewQueryCleanupLogModel(db, cacheConf)
|
||||
queryCleanupDetailModel := model.NewQueryCleanupDetailModel(db, cacheConf)
|
||||
@@ -217,6 +219,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
// 订单相关模型
|
||||
OrderModel: orderModel,
|
||||
QueryModel: queryModel,
|
||||
QueryUserRecordModel: queryUserRecordModel,
|
||||
OrderRefundModel: orderRefundModel,
|
||||
QueryCleanupLogModel: queryCleanupLogModel,
|
||||
QueryCleanupDetailModel: queryCleanupDetailModel,
|
||||
|
||||
@@ -61,6 +61,9 @@ type AdminGetOrderListReq struct {
|
||||
PayTimeEnd string `form:"pay_time_end,optional"` // 支付时间结束
|
||||
RefundTimeStart string `form:"refund_time_start,optional"` // 退款时间开始
|
||||
RefundTimeEnd string `form:"refund_time_end,optional"` // 退款时间结束
|
||||
QueryUserName string `form:"query_user_name,optional"` // 被查询人姓名
|
||||
QueryUserIdCard string `form:"query_user_id_card,optional"` // 被查询人身份证号
|
||||
QueryUserMobile string `form:"query_user_mobile,optional"` // 被查询人手机号
|
||||
}
|
||||
|
||||
type AdminGetOrderListResp struct {
|
||||
|
||||
27
app/main/model/queryUserRecordModel.go
Normal file
27
app/main/model/queryUserRecordModel.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
var _ QueryUserRecordModel = (*customQueryUserRecordModel)(nil)
|
||||
|
||||
type (
|
||||
// QueryUserRecordModel is an interface to be customized, add more methods here,
|
||||
// and implement the added methods in customQueryUserRecordModel.
|
||||
QueryUserRecordModel interface {
|
||||
queryUserRecordModel
|
||||
}
|
||||
|
||||
customQueryUserRecordModel struct {
|
||||
*defaultQueryUserRecordModel
|
||||
}
|
||||
)
|
||||
|
||||
// NewQueryUserRecordModel returns a model for the database table.
|
||||
func NewQueryUserRecordModel(conn sqlx.SqlConn, c cache.CacheConf) QueryUserRecordModel {
|
||||
return &customQueryUserRecordModel{
|
||||
defaultQueryUserRecordModel: newQueryUserRecordModel(conn, c),
|
||||
}
|
||||
}
|
||||
394
app/main/model/queryUserRecordModel_gen.go
Normal file
394
app/main/model/queryUserRecordModel_gen.go
Normal file
@@ -0,0 +1,394 @@
|
||||
// Code generated by goctl. DO NOT EDIT!
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"github.com/zeromicro/go-zero/core/stringx"
|
||||
"qnc-server/common/globalkey"
|
||||
)
|
||||
|
||||
var (
|
||||
queryUserRecordFieldNames = builder.RawFieldNames(&QueryUserRecord{})
|
||||
queryUserRecordRows = strings.Join(queryUserRecordFieldNames, ",")
|
||||
queryUserRecordRowsExpectAutoSet = strings.Join(stringx.Remove(queryUserRecordFieldNames, "`create_time`", "`update_time`"), ",")
|
||||
queryUserRecordRowsWithPlaceHolder = strings.Join(stringx.Remove(queryUserRecordFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
|
||||
|
||||
cacheZacQueryUserRecordIdPrefix = "cache:zac:queryUserRecord:id:"
|
||||
)
|
||||
|
||||
type (
|
||||
queryUserRecordModel interface {
|
||||
Insert(ctx context.Context, session sqlx.Session, data *QueryUserRecord) (sql.Result, error)
|
||||
FindOne(ctx context.Context, id string) (*QueryUserRecord, error)
|
||||
Update(ctx context.Context, session sqlx.Session, data *QueryUserRecord) (sql.Result, error)
|
||||
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *QueryUserRecord) error
|
||||
Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error
|
||||
SelectBuilder() squirrel.SelectBuilder
|
||||
DeleteSoft(ctx context.Context, session sqlx.Session, data *QueryUserRecord) error
|
||||
FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error)
|
||||
FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error)
|
||||
FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*QueryUserRecord, error)
|
||||
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*QueryUserRecord, error)
|
||||
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*QueryUserRecord, int64, error)
|
||||
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*QueryUserRecord, error)
|
||||
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*QueryUserRecord, error)
|
||||
Delete(ctx context.Context, session sqlx.Session, id string) error
|
||||
}
|
||||
|
||||
defaultQueryUserRecordModel struct {
|
||||
sqlc.CachedConn
|
||||
table string
|
||||
}
|
||||
|
||||
QueryUserRecord struct {
|
||||
Id string `db:"id"` // 主键ID(UUID)
|
||||
CreateTime time.Time `db:"create_time"` // 创建时间
|
||||
UpdateTime time.Time `db:"update_time"` // 更新时间
|
||||
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
|
||||
DelState int64 `db:"del_state"` // 删除状态:0-未删除,1-已删除
|
||||
Version int64 `db:"version"` // 版本号
|
||||
UserId string `db:"user_id"` // 用户ID(UUID,关联 user 表)
|
||||
Name string `db:"name"` // 姓名密文(AES-ECB+Base64),用于按姓名筛选订单
|
||||
IdCard string `db:"id_card"` // 身份证号密文(AES-ECB+Base64),用于按身份证号筛选订单
|
||||
Mobile string `db:"mobile"` // 手机号密文(AES-ECB+Base64),用于按手机号筛选订单
|
||||
Product string `db:"product"` // 产品类型,如 marriage/homeservice/riskassessment 等
|
||||
QueryNo string `db:"query_no"` // 查询单号(与 order.order_no 一致,如 Q_xxx),用户提交查询时生成
|
||||
OrderId sql.NullString `db:"order_id"` // 订单ID(UUID,关联 order 表),用户发起支付并创建订单后写入
|
||||
PlatformOrderId sql.NullString `db:"platform_order_id"` // 支付平台订单号(支付宝/微信),支付成功后由回调写入
|
||||
AgentIdentifier sql.NullString `db:"agent_identifier"` // 代理标识,代理渠道时有值
|
||||
}
|
||||
)
|
||||
|
||||
func newQueryUserRecordModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultQueryUserRecordModel {
|
||||
return &defaultQueryUserRecordModel{
|
||||
CachedConn: sqlc.NewConn(conn, c),
|
||||
table: "`query_user_record`",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) Insert(ctx context.Context, session sqlx.Session, data *QueryUserRecord) (sql.Result, error) {
|
||||
data.DelState = globalkey.DelStateNo
|
||||
m.insertUUID(data)
|
||||
zacQueryUserRecordIdKey := fmt.Sprintf("%s%v", cacheZacQueryUserRecordIdPrefix, data.Id)
|
||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, queryUserRecordRowsExpectAutoSet)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier)
|
||||
}, zacQueryUserRecordIdKey)
|
||||
}
|
||||
func (m *defaultQueryUserRecordModel) insertUUID(data *QueryUserRecord) {
|
||||
t := reflect.TypeOf(data).Elem()
|
||||
v := reflect.ValueOf(data).Elem()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
sf := t.Field(i)
|
||||
if sf.Tag.Get("db") == "id" {
|
||||
f := v.Field(i)
|
||||
if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
|
||||
if f.String() == "" {
|
||||
f.SetString(uuid.NewString())
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) FindOne(ctx context.Context, id string) (*QueryUserRecord, error) {
|
||||
zacQueryUserRecordIdKey := fmt.Sprintf("%s%v", cacheZacQueryUserRecordIdPrefix, id)
|
||||
var resp QueryUserRecord
|
||||
err := m.QueryRowCtx(ctx, &resp, zacQueryUserRecordIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", queryUserRecordRows, m.table)
|
||||
return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo)
|
||||
})
|
||||
switch err {
|
||||
case nil:
|
||||
return &resp, nil
|
||||
case sqlc.ErrNotFound:
|
||||
return nil, ErrNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) Update(ctx context.Context, session sqlx.Session, data *QueryUserRecord) (sql.Result, error) {
|
||||
zacQueryUserRecordIdKey := fmt.Sprintf("%s%v", cacheZacQueryUserRecordIdPrefix, data.Id)
|
||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, queryUserRecordRowsWithPlaceHolder)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier, data.Id)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier, data.Id)
|
||||
}, zacQueryUserRecordIdKey)
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *QueryUserRecord) error {
|
||||
|
||||
oldVersion := data.Version
|
||||
data.Version += 1
|
||||
|
||||
var sqlResult sql.Result
|
||||
var err error
|
||||
|
||||
zacQueryUserRecordIdKey := fmt.Sprintf("%s%v", cacheZacQueryUserRecordIdPrefix, data.Id)
|
||||
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, queryUserRecordRowsWithPlaceHolder)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier, data.Id, oldVersion)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier, data.Id, oldVersion)
|
||||
}, zacQueryUserRecordIdKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateCount, err := sqlResult.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if updateCount == 0 {
|
||||
return ErrNoRowsUpdate
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *QueryUserRecord) error {
|
||||
data.DelState = globalkey.DelStateYes
|
||||
data.DeleteTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if err := m.UpdateWithVersion(ctx, session, data); err != nil {
|
||||
return errors.Wrapf(errors.New("delete soft failed "), "QueryUserRecordModel delete err : %+v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) FindSum(ctx context.Context, builder squirrel.SelectBuilder, field string) (float64, error) {
|
||||
|
||||
if len(field) == 0 {
|
||||
return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field")
|
||||
}
|
||||
|
||||
builder = builder.Columns("IFNULL(SUM(" + field + "),0)")
|
||||
|
||||
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var resp float64
|
||||
err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64, error) {
|
||||
|
||||
if len(field) == 0 {
|
||||
return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field")
|
||||
}
|
||||
|
||||
builder = builder.Columns("COUNT(" + field + ")")
|
||||
|
||||
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var resp int64
|
||||
err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*QueryUserRecord, error) {
|
||||
|
||||
builder = builder.Columns(queryUserRecordRows)
|
||||
|
||||
if orderBy == "" {
|
||||
builder = builder.OrderBy("id DESC")
|
||||
} else {
|
||||
builder = builder.OrderBy(orderBy)
|
||||
}
|
||||
|
||||
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp []*QueryUserRecord
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*QueryUserRecord, error) {
|
||||
|
||||
builder = builder.Columns(queryUserRecordRows)
|
||||
|
||||
if orderBy == "" {
|
||||
builder = builder.OrderBy("id DESC")
|
||||
} else {
|
||||
builder = builder.OrderBy(orderBy)
|
||||
}
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp []*QueryUserRecord
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*QueryUserRecord, int64, error) {
|
||||
|
||||
total, err := m.FindCount(ctx, builder, "id")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
builder = builder.Columns(queryUserRecordRows)
|
||||
|
||||
if orderBy == "" {
|
||||
builder = builder.OrderBy("id DESC")
|
||||
} else {
|
||||
builder = builder.OrderBy(orderBy)
|
||||
}
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
|
||||
if err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
|
||||
var resp []*QueryUserRecord
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, total, nil
|
||||
default:
|
||||
return nil, total, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*QueryUserRecord, error) {
|
||||
|
||||
builder = builder.Columns(queryUserRecordRows)
|
||||
|
||||
if preMinId > 0 {
|
||||
builder = builder.Where(" id < ? ", preMinId)
|
||||
}
|
||||
|
||||
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp []*QueryUserRecord
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*QueryUserRecord, error) {
|
||||
|
||||
builder = builder.Columns(queryUserRecordRows)
|
||||
|
||||
if preMaxId > 0 {
|
||||
builder = builder.Where(" id > ? ", preMaxId)
|
||||
}
|
||||
|
||||
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp []*QueryUserRecord
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error {
|
||||
|
||||
return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
return fn(ctx, session)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) SelectBuilder() squirrel.SelectBuilder {
|
||||
return squirrel.Select().From(m.table)
|
||||
}
|
||||
func (m *defaultQueryUserRecordModel) Delete(ctx context.Context, session sqlx.Session, id string) error {
|
||||
zacQueryUserRecordIdKey := fmt.Sprintf("%s%v", cacheZacQueryUserRecordIdPrefix, id)
|
||||
_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, id)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, id)
|
||||
}, zacQueryUserRecordIdKey)
|
||||
return err
|
||||
}
|
||||
func (m *defaultQueryUserRecordModel) formatPrimary(primary interface{}) string {
|
||||
return fmt.Sprintf("%s%v", cacheZacQueryUserRecordIdPrefix, primary)
|
||||
}
|
||||
func (m *defaultQueryUserRecordModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", queryUserRecordRows, m.table)
|
||||
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
|
||||
}
|
||||
|
||||
func (m *defaultQueryUserRecordModel) tableName() string {
|
||||
return m.table
|
||||
}
|
||||
@@ -28,7 +28,8 @@ $tables = @(
|
||||
# "admin_user_role",
|
||||
# "agent",
|
||||
# "agent_commission",
|
||||
"agent_config"
|
||||
# "agent_config"
|
||||
"query_user_record"
|
||||
# "agent_freeze_task",
|
||||
# "agent_invite_code",
|
||||
# "agent_invite_code_usage",
|
||||
@@ -62,7 +63,7 @@ $tables = @(
|
||||
# 为每个表生成模型
|
||||
foreach ($table in $tables) {
|
||||
Write-Host "正在生成表: $table" -ForegroundColor Green
|
||||
goctl model mysql datasource -url="qnc:5vg67b3UNHu8@tcp(127.0.0.1:21001)/qnc" -table="$table" -dir="./model" --home="$HOME_DIR" -cache=true --style=goZero
|
||||
goctl model mysql datasource -url="zac:5vg67b3UNHu8@tcp(127.0.0.1:21201)/zac" -table="$table" -dir="./model" --home="$HOME_DIR" -cache=true --style=goZero
|
||||
|
||||
# 移动生成的文件到目标目录
|
||||
if (Test-Path $OUTPUT_DIR) {
|
||||
|
||||
@@ -1,320 +0,0 @@
|
||||
# 表结构修改说明
|
||||
|
||||
本文档说明系统重构需要的所有表结构修改,包括:
|
||||
1. UUID迁移(将所有bigint类型的ID改为CHAR(36)类型的UUID)
|
||||
2. 用户系统重构(删除UserTemp表,优化UserAuth表)
|
||||
|
||||
## 一、UUID迁移
|
||||
|
||||
### 1.1 概述
|
||||
|
||||
将所有表的主键ID从`bigint`类型改为`CHAR(36)`类型的UUID,同时将所有外键关联字段也改为UUID类型。
|
||||
|
||||
**注意**:开发环境使用简化版脚本(`uuid_migration_simple.sql`),直接修改表结构,不保留旧数据。
|
||||
|
||||
### 1.2 涉及的表
|
||||
|
||||
**核心业务表**:
|
||||
- `user` - 用户表
|
||||
- `agent` - 代理表
|
||||
- `product` - 产品表
|
||||
- `order` - 订单表
|
||||
- `query` - 查询报告表
|
||||
- `user_auth` - 用户认证表
|
||||
|
||||
**代理相关表**:
|
||||
- `agent_commission` - 代理佣金表
|
||||
- `agent_invite_code` - 代理邀请码表
|
||||
- `agent_link` - 代理链接表
|
||||
- `agent_order` - 代理订单表
|
||||
- `agent_wallet` - 代理钱包表
|
||||
- `agent_withdrawal` - 代理提现表
|
||||
- `agent_withdrawal_tax` - 代理提现税费表
|
||||
- `agent_rebate` - 代理返利表
|
||||
- `agent_relation` - 代理关系表
|
||||
- `agent_upgrade` - 代理升级表
|
||||
- `agent_real_name` - 代理实名表
|
||||
- `agent_config` - 代理配置表
|
||||
- `agent_product_config` - 代理产品配置表
|
||||
- `agent_short_link` - 代理短链表
|
||||
- `agent_invite_code_usage` - 邀请码使用记录表
|
||||
- `agent_freeze_task` - 代理冻结任务表
|
||||
|
||||
**订单相关表**:
|
||||
- `order_refund` - 订单退款表
|
||||
|
||||
**查询相关表**:
|
||||
- `query_cleanup_log` - 查询清理日志表
|
||||
- `query_cleanup_detail` - 查询清理详情表
|
||||
- `query_cleanup_config` - 查询清理配置表
|
||||
|
||||
**产品相关表**:
|
||||
- `product_feature` - 产品功能表
|
||||
- `feature` - 功能表
|
||||
|
||||
**其他表**:
|
||||
- `authorization_document` - 授权书表
|
||||
- `global_notifications` - 全局通知表
|
||||
|
||||
**管理后台表**:
|
||||
- `admin_user` - 管理员用户表
|
||||
- `admin_role` - 管理员角色表
|
||||
- `admin_menu` - 管理员菜单表
|
||||
- `admin_api` - 管理员API表
|
||||
- `admin_dict_type` - 字典类型表
|
||||
- `admin_dict_data` - 字典数据表
|
||||
- `admin_user_role` - 管理员用户角色关联表
|
||||
- `admin_role_menu` - 管理员角色菜单关联表
|
||||
- `admin_role_api` - 管理员角色API关联表
|
||||
|
||||
### 1.3 修改内容
|
||||
|
||||
**主键字段**:
|
||||
- 所有表的`id`字段:`bigint` → `CHAR(36)`
|
||||
- 移除`AUTO_INCREMENT`属性
|
||||
- 插入时使用`UUID()`函数或应用层生成UUID
|
||||
|
||||
**外键字段**(软关联):
|
||||
- `user_id`:`bigint` → `CHAR(36)`
|
||||
- `agent_id`:`bigint` → `CHAR(36)`
|
||||
- `order_id`:`bigint` → `CHAR(36)`
|
||||
- `product_id`:`bigint` → `CHAR(36)`
|
||||
- `invite_code_id`:`bigint` → `CHAR(36)`
|
||||
- `link_id`:`bigint` → `CHAR(36)`
|
||||
- `commission_id`:`bigint` → `CHAR(36)`
|
||||
- `wallet_id`:`bigint` → `CHAR(36)`
|
||||
- `withdrawal_id`:`bigint` → `CHAR(36)`
|
||||
- `parent_agent_id`:`bigint` → `CHAR(36)`
|
||||
- `team_leader_id`:`bigint` → `CHAR(36)`
|
||||
- `cleanup_log_id`:`bigint` → `CHAR(36)`
|
||||
- `query_id`:`bigint` → `CHAR(36)`
|
||||
- `feature_id`:`bigint` → `CHAR(36)`
|
||||
- `parent_id`(菜单):`bigint` → `CHAR(36)`
|
||||
- `dict_type_id`:`bigint` → `CHAR(36)`
|
||||
- `role_id`:`bigint` → `CHAR(36)`
|
||||
- `menu_id`:`bigint` → `CHAR(36)`
|
||||
- `api_id`:`bigint` → `CHAR(36)`
|
||||
- `used_user_id`:`bigint` → `CHAR(36)`
|
||||
- `used_agent_id`:`bigint` → `CHAR(36)`
|
||||
|
||||
### 1.4 执行脚本
|
||||
|
||||
**开发环境**:执行文件 `uuid_migration_simple.sql`
|
||||
- 直接修改表结构,不保留旧数据
|
||||
- 适用于开发环境,没有重要数据
|
||||
|
||||
**生产环境**:执行文件 `uuid_migration.sql`
|
||||
- 完整的迁移流程,保留旧数据以便回滚
|
||||
- 需要分阶段执行,包含数据迁移和验证
|
||||
|
||||
**开发环境执行步骤**:
|
||||
1. 直接执行 `uuid_migration_simple.sql`
|
||||
2. 所有表的主键和外键字段直接改为CHAR(36)
|
||||
3. 插入新记录时,在应用层生成UUID
|
||||
|
||||
**注意事项**:
|
||||
- 开发环境脚本直接修改表结构,不保留旧数据
|
||||
- 如果表中有数据,需要先清空数据或手动填充UUID
|
||||
- 代码层面需要同步修改所有ID字段类型(int64 → string)
|
||||
|
||||
## 二、用户系统重构
|
||||
|
||||
### 2.1 概述
|
||||
|
||||
删除`UserTemp`表,统一使用`User`表(通过`mobile`字段是否为空区分临时用户和正式用户),优化`UserAuth`表结构。
|
||||
|
||||
### 2.2 涉及的表
|
||||
|
||||
**需要修改的表**:
|
||||
- `user` - 用户表(无需修改结构,只需确保mobile可为空)
|
||||
- `user_auth` - 用户认证表(添加唯一索引)
|
||||
- `user_temp` - 临时用户表(**删除**)
|
||||
|
||||
**需要迁移数据的表**:
|
||||
- `order` - 订单表(更新user_id)
|
||||
- `query` - 查询报告表(更新user_id)
|
||||
|
||||
### 2.3 修改内容
|
||||
|
||||
#### 2.3.1 UserAuth表
|
||||
|
||||
**添加唯一索引**:
|
||||
```sql
|
||||
ALTER TABLE `user_auth`
|
||||
ADD UNIQUE INDEX `uk_auth_type_key` (`auth_type`, `auth_key`);
|
||||
```
|
||||
|
||||
**作用**:
|
||||
- 确保同一个认证方式(auth_type + auth_key)只能绑定一个用户
|
||||
- 防止重复绑定
|
||||
|
||||
#### 2.3.2 UserTemp表
|
||||
|
||||
**删除原因**:
|
||||
- 临时用户可以直接创建User表记录(mobile为空)
|
||||
- 临时用户的认证信息存储在UserAuth表
|
||||
- 通过User.mobile是否为空来区分临时用户和正式用户
|
||||
- 统一使用User表,逻辑更清晰
|
||||
|
||||
**删除步骤**:
|
||||
1. 迁移UserTemp数据到User和UserAuth表
|
||||
2. 更新关联的订单和报告的user_id
|
||||
3. 验证数据完整性
|
||||
4. 删除UserTemp表
|
||||
|
||||
### 2.4 执行脚本
|
||||
|
||||
**开发环境**:执行文件 `user_system_refactor.sql`(已简化)
|
||||
- 直接删除UserTemp表
|
||||
- 添加UserAuth唯一索引
|
||||
|
||||
**生产环境**:如需数据迁移,请参考完整版脚本
|
||||
|
||||
**开发环境执行步骤**:
|
||||
1. 添加UserAuth唯一索引
|
||||
2. 直接删除UserTemp表
|
||||
|
||||
**注意事项**:
|
||||
- 开发环境脚本直接删除UserTemp表,不进行数据迁移
|
||||
- 删除UserTemp表前,确保代码中已移除所有对UserTemp表的引用
|
||||
|
||||
## 三、执行顺序
|
||||
|
||||
### 3.1 推荐执行顺序
|
||||
|
||||
**开发环境**:
|
||||
1. **先执行用户系统重构**(`user_system_refactor.sql`)
|
||||
- 添加UserAuth唯一索引
|
||||
- 删除UserTemp表
|
||||
|
||||
2. **再执行UUID迁移**(`uuid_migration_simple.sql`)
|
||||
- 直接修改所有表结构
|
||||
- 需要同步修改代码
|
||||
|
||||
**生产环境**:
|
||||
1. 先执行用户系统重构(完整版,包含数据迁移)
|
||||
2. 再执行UUID迁移(完整版,包含数据迁移和验证)
|
||||
|
||||
### 3.2 如果先执行UUID迁移
|
||||
|
||||
如果先执行UUID迁移,需要注意:
|
||||
- UserTemp表的ID也会改为UUID
|
||||
- 迁移UserTemp数据时,需要使用UUID映射
|
||||
- 用户系统重构脚本需要相应调整
|
||||
|
||||
## 四、代码修改清单
|
||||
|
||||
### 4.1 UUID迁移需要的代码修改
|
||||
|
||||
**Model层**:
|
||||
- 所有Model的ID字段类型:`int64` → `string`
|
||||
- 所有外键字段类型:`int64` → `string`
|
||||
- 移除`AUTO_INCREMENT`相关逻辑
|
||||
- 插入时生成UUID(使用`uuid.NewString()`)
|
||||
|
||||
**Service层**:
|
||||
- 所有使用ID的地方改为UUID
|
||||
- 查询条件改为UUID
|
||||
- 关联查询改为UUID
|
||||
|
||||
**API层**:
|
||||
- 请求参数中的ID改为UUID
|
||||
- 响应数据中的ID改为UUID
|
||||
|
||||
**数据库操作**:
|
||||
- 插入操作:使用`uuid.NewString()`生成UUID
|
||||
- 查询操作:使用UUID字符串查询
|
||||
- 更新操作:使用UUID字符串更新
|
||||
|
||||
### 4.2 用户系统重构需要的代码修改
|
||||
|
||||
**删除UserTemp相关代码**:
|
||||
- 删除`UserTempModel`
|
||||
- 删除所有使用`UserTemp`的代码
|
||||
- 删除临时用户创建逻辑中的UserTemp相关代码
|
||||
|
||||
**修改用户创建逻辑**:
|
||||
- 临时用户:直接创建User记录(mobile为空)
|
||||
- 创建UserAuth记录(auth_type + auth_key)
|
||||
|
||||
**修改用户查询逻辑**:
|
||||
- 通过UserAuth表查询用户
|
||||
- 通过User.mobile是否为空判断用户类型
|
||||
|
||||
**修改账号合并逻辑**:
|
||||
- 绑定手机号时,检查手机号是否已存在
|
||||
- 如果存在,合并账号(迁移数据)
|
||||
- 如果不存在,更新User.mobile
|
||||
|
||||
## 五、验证清单
|
||||
|
||||
### 5.1 UUID迁移验证
|
||||
|
||||
- [ ] 所有表的主键已改为CHAR(36)
|
||||
- [ ] 所有外键字段已改为CHAR(36)
|
||||
- [ ] 插入新记录时自动生成UUID
|
||||
- [ ] 查询操作使用UUID正常
|
||||
- [ ] 关联查询使用UUID正常
|
||||
- [ ] 索引和约束已更新
|
||||
- [ ] 数据完整性验证通过
|
||||
|
||||
### 5.2 用户系统重构验证
|
||||
|
||||
- [ ] UserAuth表有唯一索引`uk_auth_type_key`
|
||||
- [ ] UserTemp表数据已迁移(如果存在)
|
||||
- [ ] 订单和报告的user_id已更新
|
||||
- [ ] UserTemp表已删除(如果存在)
|
||||
- [ ] 临时用户创建逻辑使用User表
|
||||
- [ ] 账号合并逻辑正常工作
|
||||
- [ ] 数据完整性验证通过
|
||||
|
||||
## 六、回滚方案
|
||||
|
||||
### 6.1 UUID迁移回滚
|
||||
|
||||
如果UUID迁移出现问题,可以:
|
||||
1. 保留原ID字段(迁移脚本中已保留)
|
||||
2. 恢复主键为原ID字段
|
||||
3. 删除UUID字段
|
||||
4. 恢复代码使用int64类型
|
||||
|
||||
### 6.2 用户系统重构回滚
|
||||
|
||||
如果用户系统重构出现问题,可以:
|
||||
1. 恢复UserTemp表(从备份)
|
||||
2. 恢复订单和报告的user_id
|
||||
3. 恢复代码使用UserTemp表
|
||||
|
||||
## 七、常见问题
|
||||
|
||||
### 7.1 UUID性能问题
|
||||
|
||||
**问题**:UUID作为主键可能影响性能
|
||||
|
||||
**解答**:
|
||||
- UUID是字符串类型,索引效率略低于bigint
|
||||
- 但UUID的优势是全局唯一,适合分布式系统
|
||||
- 如果性能问题严重,可以考虑使用BINARY(16)存储UUID(需要转换)
|
||||
|
||||
### 7.2 UUID长度问题
|
||||
|
||||
**问题**:CHAR(36)占用空间较大
|
||||
|
||||
**解答**:
|
||||
- UUID标准格式是36字符(包含连字符)
|
||||
- 如果空间敏感,可以使用BINARY(16)存储(需要转换函数)
|
||||
- 当前使用CHAR(36)便于调试和查看
|
||||
|
||||
### 7.3 UserTemp数据迁移问题
|
||||
|
||||
**问题**:如何确保UserTemp数据正确迁移?
|
||||
|
||||
**解答**:
|
||||
- 通过auth_type和auth_key匹配UserAuth记录
|
||||
- 建立UserTemp.id到User.id的映射关系
|
||||
- 迁移后验证数据完整性
|
||||
- 保留UserTemp表一段时间,确认无误后再删除
|
||||
|
||||
## 八、联系信息
|
||||
|
||||
如有问题,请联系开发团队。
|
||||
@@ -1,47 +0,0 @@
|
||||
-- ============================================
|
||||
-- 代理短链表
|
||||
-- 说明:存储推广链接和邀请链接的短链映射关系
|
||||
--
|
||||
-- 功能说明:
|
||||
-- 1. 支持两种类型:promotion(推广报告)和 invite(邀请好友)
|
||||
-- 2. 为每个链接生成一个短链标识(6位随机字符串)
|
||||
-- 3. 短链格式:https://推广域名/s/{short_code}
|
||||
-- 4. 短链重定向到前端传入的target_path
|
||||
--
|
||||
-- 执行步骤:
|
||||
-- 1. 执行此 SQL 创建表
|
||||
-- 2. 使用 goctl 生成 Model:agent_short_link -> AgentShortLinkModel
|
||||
-- 3. 在 servicecontext.go 中添加 AgentShortLinkModel
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_short_link` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`type` tinyint NOT NULL COMMENT '类型:1=推广报告(promotion),2=邀请好友(invite)',
|
||||
`link_id` bigint DEFAULT NULL COMMENT '推广链接ID(关联agent_link表,仅推广报告类型使用)',
|
||||
`invite_code_id` bigint DEFAULT NULL COMMENT '邀请码ID(关联agent_invite_code表,仅邀请好友类型使用)',
|
||||
`link_identifier` varchar(200) DEFAULT NULL COMMENT '推广链接标识(加密,仅推广报告类型使用)',
|
||||
`invite_code` varchar(50) DEFAULT NULL COMMENT '邀请码(仅邀请好友类型使用)',
|
||||
`short_code` varchar(20) NOT NULL COMMENT '短链标识(6位随机字符串)',
|
||||
`target_path` varchar(500) NOT NULL COMMENT '目标地址(前端传入,如:/agent/promotionInquire/xxx 或 /register?invite_code=xxx)',
|
||||
`promotion_domain` varchar(200) NOT NULL COMMENT '推广域名(生成短链时使用的域名)',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_short_code` (`short_code`, `del_state`) COMMENT '短链标识唯一索引',
|
||||
UNIQUE KEY `uk_link_id_type` (
|
||||
`link_id`,
|
||||
`type`,
|
||||
`del_state`
|
||||
) COMMENT '同一推广链接同一类型只能有一个有效短链',
|
||||
UNIQUE KEY `uk_invite_code_id_type` (
|
||||
`invite_code_id`,
|
||||
`type`,
|
||||
`del_state`
|
||||
) COMMENT '同一邀请码同一类型只能有一个有效短链',
|
||||
KEY `idx_link_identifier` (`link_identifier`) COMMENT '推广链接标识索引',
|
||||
KEY `idx_invite_code` (`invite_code`) COMMENT '邀请码索引',
|
||||
KEY `idx_type` (`type`) COMMENT '类型索引',
|
||||
KEY `idx_create_time` (`create_time`) COMMENT '创建时间索引'
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '代理短链表';
|
||||
@@ -1,46 +0,0 @@
|
||||
-- ============================================
|
||||
-- 邀请码使用历史表
|
||||
-- 说明:记录每次邀请码的使用情况,支持统计和查询
|
||||
--
|
||||
-- 功能说明:
|
||||
-- 1. 记录每个邀请码每次使用的详细信息
|
||||
-- 2. 支持统计每个邀请码邀请了多少代理
|
||||
-- 3. 支持查询某个代理是通过哪个邀请码成为代理的
|
||||
-- 4. 保留完整的使用历史记录
|
||||
--
|
||||
-- 执行步骤:
|
||||
-- 1. 执行此 SQL 创建表和添加字段
|
||||
-- 2. 使用 goctl 生成 Model:agent_invite_code_usage -> AgentInviteCodeUsageModel
|
||||
-- 3. 在 servicecontext.go 中添加 AgentInviteCodeUsageModel
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_invite_code_usage` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`invite_code_id` bigint NOT NULL COMMENT '邀请码ID(关联agent_invite_code表)',
|
||||
`code` varchar(50) NOT NULL COMMENT '邀请码(冗余字段,便于查询)',
|
||||
`user_id` bigint NOT NULL COMMENT '使用用户ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '成为的代理ID',
|
||||
`agent_level` tinyint NOT NULL COMMENT '代理等级:1=普通,2=黄金,3=钻石',
|
||||
`used_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '使用时间',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_invite_code_id` (`invite_code_id`) COMMENT '关联邀请码ID',
|
||||
KEY `idx_code` (`code`) COMMENT '邀请码索引',
|
||||
KEY `idx_user_id` (`user_id`) COMMENT '用户ID索引',
|
||||
KEY `idx_agent_id` (`agent_id`) COMMENT '代理ID索引',
|
||||
KEY `idx_used_time` (`used_time`) COMMENT '使用时间索引',
|
||||
KEY `idx_create_time` (`create_time`) COMMENT '创建时间索引',
|
||||
KEY `idx_code_time` (`code`, `used_time`) COMMENT '邀请码和使用时间复合索引'
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '邀请码使用历史表';
|
||||
|
||||
-- ============================================
|
||||
-- 在agent表中添加invite_code_id字段(可选,便于直接查询代理是通过哪个邀请码成为的)
|
||||
-- ============================================
|
||||
ALTER TABLE `agent`
|
||||
ADD COLUMN `invite_code_id` bigint DEFAULT NULL COMMENT '通过哪个邀请码成为代理(关联agent_invite_code表)' AFTER `team_leader_id`;
|
||||
|
||||
ALTER TABLE `agent`
|
||||
ADD KEY `idx_invite_code_id` (`invite_code_id`) COMMENT '邀请码ID索引';
|
||||
@@ -1,56 +0,0 @@
|
||||
-- ============================================
|
||||
-- 代理配置表重构 SQL 脚本
|
||||
-- 执行顺序:按照下面的顺序依次执行
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- 步骤1:删除价格相关配置项
|
||||
-- 说明:价格配置已改为在产品配置表(agent_product_config)中按产品配置
|
||||
-- ============================================
|
||||
DELETE FROM `agent_config` WHERE `config_key` IN (
|
||||
'base_price',
|
||||
'system_max_price',
|
||||
'price_threshold',
|
||||
'price_fee_rate'
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- 步骤2:统一配置键命名
|
||||
-- 说明:修改配置键名称,使其与代码逻辑一致
|
||||
-- ============================================
|
||||
|
||||
-- 修改等级加成配置键
|
||||
UPDATE `agent_config` SET `config_key` = 'level_1_bonus' WHERE `config_key` = 'level_bonus_normal';
|
||||
UPDATE `agent_config` SET `config_key` = 'level_2_bonus' WHERE `config_key` = 'level_bonus_gold';
|
||||
UPDATE `agent_config` SET `config_key` = 'level_3_bonus' WHERE `config_key` = 'level_bonus_diamond';
|
||||
|
||||
-- 修改升级费用配置键
|
||||
UPDATE `agent_config` SET `config_key` = 'upgrade_to_gold_fee' WHERE `config_key` = 'upgrade_fee_normal_to_gold';
|
||||
UPDATE `agent_config` SET `config_key` = 'upgrade_to_diamond_fee' WHERE `config_key` = 'upgrade_fee_to_diamond';
|
||||
|
||||
-- 修改升级返佣配置键
|
||||
UPDATE `agent_config` SET `config_key` = 'upgrade_to_gold_rebate' WHERE `config_key` = 'upgrade_rebate_normal_to_gold';
|
||||
UPDATE `agent_config` SET `config_key` = 'upgrade_to_diamond_rebate' WHERE `config_key` = 'upgrade_rebate_to_diamond';
|
||||
|
||||
-- ============================================
|
||||
-- 步骤3:添加缺失的配置项
|
||||
-- 说明:添加免税额度配置项(如果不存在)
|
||||
-- ============================================
|
||||
INSERT INTO `agent_config` (`config_key`, `config_value`, `config_type`, `description`)
|
||||
SELECT 'tax_exemption_amount', '0.00', 'tax', '提现免税额度(元,默认0)'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `agent_config` WHERE `config_key` = 'tax_exemption_amount'
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- 验证查询:检查配置项是否正确
|
||||
-- ============================================
|
||||
SELECT `config_key`, `config_value`, `config_type`, `description`
|
||||
FROM `agent_config`
|
||||
WHERE `del_state` = 0
|
||||
ORDER BY `config_type`, `config_key`;
|
||||
|
||||
-- ============================================
|
||||
-- 执行完成后,请重新生成 agent_config 相关的 Model 代码
|
||||
-- ============================================
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
-- ============================================
|
||||
-- 代理佣金冻结任务表
|
||||
-- 说明:用于记录需要解冻的佣金冻结任务,保证异步任务的一致性和持久化
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE `agent_freeze_task` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '代理ID',
|
||||
`order_id` bigint NOT NULL COMMENT '订单ID',
|
||||
`commission_id` bigint NOT NULL COMMENT '佣金记录ID',
|
||||
`freeze_amount` decimal(10, 2) NOT NULL COMMENT '冻结金额',
|
||||
`order_price` decimal(10, 2) NOT NULL COMMENT '订单单价',
|
||||
`freeze_ratio` decimal(5, 4) NOT NULL DEFAULT 0.1000 COMMENT '冻结比例(例如:0.1000表示10%)',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1=待解冻,2=已解冻,3=已取消',
|
||||
`freeze_time` datetime NOT NULL COMMENT '冻结时间',
|
||||
`unfreeze_time` datetime NOT NULL COMMENT '解冻时间(冻结时间+1个月)',
|
||||
`actual_unfreeze_time` datetime DEFAULT NULL COMMENT '实际解冻时间',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_agent_id` (`agent_id`),
|
||||
KEY `idx_order_id` (`order_id`),
|
||||
KEY `idx_commission_id` (`commission_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_unfreeze_time` (`unfreeze_time`),
|
||||
KEY `idx_agent_status` (`agent_id`, `status`),
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '代理佣金冻结任务表';
|
||||
@@ -1,386 +0,0 @@
|
||||
-- ============================================
|
||||
-- 新代理系统数据表创建脚本
|
||||
-- 说明:创建新代理系统的所有数据表
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- 1. 代理基本信息表
|
||||
-- ============================================
|
||||
CREATE TABLE `agent` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`level` tinyint NOT NULL DEFAULT 1 COMMENT '代理等级:1=普通,2=黄金,3=钻石',
|
||||
`region` varchar(50) DEFAULT NULL COMMENT '区域(可选)',
|
||||
`mobile` varchar(50) NOT NULL DEFAULT '' COMMENT '手机号(加密)',
|
||||
`wechat_id` varchar(100) DEFAULT NULL COMMENT '微信号',
|
||||
`team_leader_id` bigint DEFAULT NULL COMMENT '团队首领ID(钻石代理的ID,普通/黄金代理指向其团队首领)',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_user_id` (`user_id`),
|
||||
KEY `idx_mobile` (`mobile`),
|
||||
KEY `idx_level` (`level`),
|
||||
KEY `idx_team_leader_id` (`team_leader_id`),
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理基本信息表';
|
||||
|
||||
-- ============================================
|
||||
-- 2. 代理钱包表(注意:agent_audit 表已废弃,新系统通过邀请码直接成为代理,无需审核)
|
||||
-- ============================================
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_wallet` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '代理ID',
|
||||
`balance` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '可用余额',
|
||||
`frozen_balance` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '冻结余额',
|
||||
`total_earnings` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '累计收益',
|
||||
`withdrawn_amount` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '累计提现金额',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_agent_id` (`agent_id`),
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理钱包表';
|
||||
|
||||
-- ============================================
|
||||
-- 4. 代理上下级关系表(支持脱离关系)
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_relation` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`parent_id` bigint NOT NULL COMMENT '上级代理ID',
|
||||
`child_id` bigint NOT NULL COMMENT '下级代理ID',
|
||||
`relation_type` tinyint NOT NULL DEFAULT 1 COMMENT '关系类型:1=直接关系,2=已脱离(历史记录)',
|
||||
`detach_reason` varchar(100) DEFAULT NULL COMMENT '脱离原因:upgrade=升级脱离',
|
||||
`detach_time` datetime DEFAULT NULL COMMENT '脱离时间',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_parent_child_type` (`parent_id`, `child_id`, `relation_type`),
|
||||
KEY `idx_parent_id` (`parent_id`),
|
||||
KEY `idx_child_id` (`child_id`),
|
||||
KEY `idx_relation_type` (`relation_type`),
|
||||
KEY `idx_parent_relation` (`parent_id`, `relation_type`) COMMENT '复合索引:查询有效下级',
|
||||
KEY `idx_child_relation` (`child_id`, `relation_type`) COMMENT '复合索引:查询有效上级',
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理上下级关系表';
|
||||
|
||||
-- ============================================
|
||||
-- 5. 代理推广链接表
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_link` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '代理ID',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`product_id` bigint NOT NULL COMMENT '产品ID',
|
||||
`link_identifier` varchar(200) NOT NULL COMMENT '推广链接标识(加密)',
|
||||
`set_price` decimal(10,2) NOT NULL COMMENT '代理设定价格',
|
||||
`actual_base_price` decimal(10,2) NOT NULL COMMENT '实际底价(基础底价+等级加成)',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_link_identifier` (`link_identifier`),
|
||||
UNIQUE KEY `uk_agent_product_price` (`agent_id`, `product_id`, `set_price`, `del_state`) COMMENT '唯一约束:同一代理、同一产品、同一价格只能有一个有效链接',
|
||||
KEY `idx_agent_id` (`agent_id`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_product_id` (`product_id`),
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理推广链接表';
|
||||
|
||||
-- ============================================
|
||||
-- 6. 代理订单关联表
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_order` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '代理ID',
|
||||
`order_id` bigint NOT NULL COMMENT '订单ID',
|
||||
`product_id` bigint NOT NULL COMMENT '产品ID',
|
||||
`order_amount` decimal(10,2) NOT NULL COMMENT '订单金额(用户实际支付金额,冗余字段)',
|
||||
`set_price` decimal(10,2) NOT NULL COMMENT '代理设定价格',
|
||||
`actual_base_price` decimal(10,2) NOT NULL COMMENT '实际底价(基础底价+等级加成)',
|
||||
`price_cost` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '提价成本((设定价格-提价标准阈值)×提价手续费比例)',
|
||||
`agent_profit` decimal(10,2) NOT NULL COMMENT '代理收益(设定价格-实际底价-提价成本)',
|
||||
`process_status` tinyint NOT NULL DEFAULT 0 COMMENT '处理状态:0=待处理,1=处理成功,2=处理失败',
|
||||
`process_time` datetime DEFAULT NULL COMMENT '处理时间',
|
||||
`process_remark` varchar(500) DEFAULT NULL COMMENT '处理备注',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_order_id` (`order_id`),
|
||||
KEY `idx_agent_id` (`agent_id`),
|
||||
KEY `idx_product_id` (`product_id`),
|
||||
KEY `idx_process_status` (`process_status`),
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理订单关联表';
|
||||
|
||||
-- ============================================
|
||||
-- 7. 代理佣金记录表
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_commission` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '代理ID',
|
||||
`order_id` bigint NOT NULL COMMENT '订单ID',
|
||||
`product_id` bigint NOT NULL COMMENT '产品ID',
|
||||
`amount` decimal(10,2) NOT NULL COMMENT '佣金金额',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1=已发放,2=已冻结,3=已取消',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_agent_id` (`agent_id`),
|
||||
KEY `idx_order_id` (`order_id`),
|
||||
KEY `idx_product_id` (`product_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_agent_status` (`agent_id`, `status`) COMMENT '复合索引:查询代理的佣金记录',
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理佣金记录表';
|
||||
|
||||
-- ============================================
|
||||
-- 8. 代理返佣记录表(等级加成返佣)
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_rebate` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '获得返佣的代理ID',
|
||||
`source_agent_id` bigint NOT NULL COMMENT '来源代理ID(推广订单的代理)',
|
||||
`order_id` bigint NOT NULL COMMENT '订单ID',
|
||||
`product_id` bigint NOT NULL COMMENT '产品ID',
|
||||
`rebate_type` tinyint NOT NULL COMMENT '返佣类型:1=直接上级返佣,2=钻石上级返佣,3=黄金上级返佣',
|
||||
`level_bonus` decimal(10,2) NOT NULL COMMENT '等级加成金额(来源代理的等级加成)',
|
||||
`rebate_amount` decimal(10,2) NOT NULL COMMENT '返佣金额',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1=已发放,2=已冻结,3=已取消',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_agent_id` (`agent_id`),
|
||||
KEY `idx_source_agent_id` (`source_agent_id`),
|
||||
KEY `idx_order_id` (`order_id`),
|
||||
KEY `idx_product_id` (`product_id`),
|
||||
KEY `idx_rebate_type` (`rebate_type`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_order_rebate_type` (`order_id`, `rebate_type`) COMMENT '复合索引:查询订单的返佣明细',
|
||||
KEY `idx_agent_status` (`agent_id`, `status`) COMMENT '复合索引:查询代理的返佣记录',
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理返佣记录表(等级加成返佣)';
|
||||
|
||||
-- ============================================
|
||||
-- 9. 代理升级记录表
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_upgrade` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '被升级的代理ID',
|
||||
`from_level` tinyint NOT NULL COMMENT '原等级:1=普通,2=黄金,3=钻石',
|
||||
`to_level` tinyint NOT NULL COMMENT '目标等级:1=普通,2=黄金,3=钻石',
|
||||
`upgrade_type` tinyint NOT NULL COMMENT '升级类型:1=自主付费,2=钻石升级下级',
|
||||
`upgrade_fee` decimal(10,2) DEFAULT 0.00 COMMENT '升级费用',
|
||||
`rebate_amount` decimal(10,2) DEFAULT 0.00 COMMENT '返佣金额(给原直接上级)',
|
||||
`rebate_agent_id` bigint DEFAULT NULL COMMENT '返佣代理ID(原直接上级)',
|
||||
`operator_agent_id` bigint DEFAULT NULL COMMENT '操作代理ID(如果是钻石升级下级,记录操作者)',
|
||||
`order_no` varchar(100) DEFAULT NULL COMMENT '支付订单号(如果是自主付费)',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1=待处理,2=已完成,3=已失败',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_agent_id` (`agent_id`),
|
||||
KEY `idx_rebate_agent_id` (`rebate_agent_id`),
|
||||
KEY `idx_operator_agent_id` (`operator_agent_id`),
|
||||
KEY `idx_upgrade_type` (`upgrade_type`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_agent_status` (`agent_id`, `status`) COMMENT '复合索引:查询代理的升级记录',
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理升级记录表';
|
||||
|
||||
-- ============================================
|
||||
-- 10. 代理提现表
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_withdrawal` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '代理ID',
|
||||
`withdraw_no` varchar(100) NOT NULL COMMENT '提现单号',
|
||||
`payee_account` varchar(100) NOT NULL COMMENT '收款账户',
|
||||
`payee_name` varchar(50) NOT NULL COMMENT '收款人姓名',
|
||||
`amount` decimal(10,2) NOT NULL COMMENT '提现金额',
|
||||
`actual_amount` decimal(10,2) NOT NULL COMMENT '实际到账金额(扣除税费后)',
|
||||
`tax_amount` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '税费金额',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1=处理中,2=成功,3=失败',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_withdraw_no` (`withdraw_no`),
|
||||
KEY `idx_agent_id` (`agent_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_agent_status` (`agent_id`, `status`) COMMENT '复合索引:查询代理的提现记录',
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理提现表';
|
||||
|
||||
-- ============================================
|
||||
-- 11. 代理系统配置表
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_config` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`config_key` varchar(100) NOT NULL COMMENT '配置键',
|
||||
`config_value` varchar(500) NOT NULL COMMENT '配置值',
|
||||
`config_type` varchar(50) NOT NULL COMMENT '配置类型:price=价格,bonus=等级加成,upgrade=升级费用,rebate=返佣,tax=税费',
|
||||
`description` varchar(500) DEFAULT NULL COMMENT '配置描述',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_config_key` (`config_key`),
|
||||
KEY `idx_config_type` (`config_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理系统配置表';
|
||||
|
||||
-- ============================================
|
||||
-- 12. 代理产品配置表(简化版)
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_product_config` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`product_id` bigint NOT NULL COMMENT '产品ID',
|
||||
`product_name` varchar(100) NOT NULL COMMENT '产品名称',
|
||||
`base_price` decimal(10,2) NOT NULL COMMENT '基础底价(BasePrice)',
|
||||
`system_max_price` decimal(10,2) NOT NULL COMMENT '系统价格上限(SystemMaxPrice)',
|
||||
`price_threshold` decimal(10,2) DEFAULT NULL COMMENT '提价标准阈值(PriceThreshold)',
|
||||
`price_fee_rate` decimal(5,4) DEFAULT NULL COMMENT '提价手续费比例(PriceFeeRate)',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_product_id` (`product_id`),
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理产品配置表';
|
||||
|
||||
-- ============================================
|
||||
-- 13. 代理实名认证表
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_real_name` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '代理ID',
|
||||
`name` varchar(50) NOT NULL COMMENT '真实姓名',
|
||||
`id_card` varchar(50) NOT NULL COMMENT '身份证号(加密)',
|
||||
`mobile` varchar(50) NOT NULL COMMENT '手机号(加密)',
|
||||
`verify_time` datetime DEFAULT NULL COMMENT '验证时间(三要素验证通过时间,NULL表示未验证)',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_agent_id` (`agent_id`),
|
||||
KEY `idx_verify_time` (`verify_time`),
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理实名认证表';
|
||||
|
||||
-- ============================================
|
||||
-- 14. 代理提现扣税记录表
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_withdrawal_tax` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`agent_id` bigint NOT NULL COMMENT '代理ID',
|
||||
`withdrawal_id` bigint NOT NULL COMMENT '提现记录ID',
|
||||
`year_month` int NOT NULL COMMENT '年月(格式:YYYYMM)',
|
||||
`withdrawal_amount` decimal(10,2) NOT NULL COMMENT '提现金额',
|
||||
`taxable_amount` decimal(10,2) NOT NULL COMMENT '应税金额',
|
||||
`tax_rate` decimal(5,4) NOT NULL COMMENT '税率',
|
||||
`tax_amount` decimal(10,2) NOT NULL COMMENT '税费金额',
|
||||
`actual_amount` decimal(10,2) NOT NULL COMMENT '实际到账金额',
|
||||
`tax_status` tinyint NOT NULL DEFAULT 1 COMMENT '扣税状态:1=待扣税,2=已扣税,3=扣税失败',
|
||||
`tax_time` datetime DEFAULT NULL COMMENT '扣税时间',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_agent_id` (`agent_id`),
|
||||
KEY `idx_withdrawal_id` (`withdrawal_id`),
|
||||
KEY `idx_year_month` (`year_month`),
|
||||
KEY `idx_tax_status` (`tax_status`),
|
||||
KEY `idx_agent_year_month` (`agent_id`, `year_month`) COMMENT '复合索引:查询代理的月度扣税记录'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理提现扣税记录表';
|
||||
|
||||
-- ============================================
|
||||
-- 初始化系统配置数据
|
||||
-- ============================================
|
||||
INSERT INTO `agent_config` (`config_key`, `config_value`, `config_type`, `description`) VALUES
|
||||
('base_price', '0.00', 'price', '系统基础底价(BasePrice)'),
|
||||
('system_max_price', '9999.99', 'price', '系统价格上限(SystemMaxPrice)'),
|
||||
('price_threshold', '0.00', 'price', '提价标准阈值(PriceThreshold)'),
|
||||
('price_fee_rate', '0.0000', 'price', '提价手续费比例(PriceFeeRate)'),
|
||||
('level_bonus_normal', '6.00', 'bonus', '普通代理等级加成(6元)'),
|
||||
('level_bonus_gold', '3.00', 'bonus', '黄金代理等级加成(3元)'),
|
||||
('level_bonus_diamond', '0.00', 'bonus', '钻石代理等级加成(0元)'),
|
||||
('upgrade_fee_normal_to_gold', '199.00', 'upgrade', '普通→黄金升级费用(199元)'),
|
||||
('upgrade_fee_to_diamond', '980.00', 'upgrade', '升级为钻石费用(980元)'),
|
||||
('upgrade_rebate_normal_to_gold', '139.00', 'upgrade', '普通→黄金返佣金额(139元)'),
|
||||
('upgrade_rebate_to_diamond', '680.00', 'upgrade', '升级为钻石返佣金额(680元)'),
|
||||
('direct_parent_amount_diamond', '6.00', 'rebate', '直接上级是钻石的返佣金额(6元)'),
|
||||
('direct_parent_amount_gold', '3.00', 'rebate', '直接上级是黄金的返佣金额(3元)'),
|
||||
('direct_parent_amount_normal', '2.00', 'rebate', '直接上级是普通的返佣金额(2元)'),
|
||||
('max_gold_rebate_amount', '3.00', 'rebate', '黄金代理最大返佣金额(3元)'),
|
||||
('tax_rate', '0.0600', 'tax', '提现税率(6%)');
|
||||
|
||||
-- ============================================
|
||||
-- 15. 代理邀请码表
|
||||
-- ============================================
|
||||
CREATE TABLE `agent_invite_code` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`code` varchar(50) NOT NULL COMMENT '邀请码(唯一)',
|
||||
`agent_id` bigint DEFAULT NULL COMMENT '发放代理ID(NULL表示平台发放的钻石邀请码)',
|
||||
`target_level` tinyint NOT NULL DEFAULT 1 COMMENT '目标等级:1=普通,2=黄金,3=钻石',
|
||||
`status` tinyint NOT NULL DEFAULT 0 COMMENT '状态:0=未使用,1=已使用,2=已失效(钻石邀请码只能使用一次,普通邀请码可无限使用)',
|
||||
`used_user_id` bigint DEFAULT NULL COMMENT '使用用户ID',
|
||||
`used_agent_id` bigint DEFAULT NULL COMMENT '使用代理ID',
|
||||
`used_time` datetime DEFAULT NULL COMMENT '使用时间',
|
||||
`expire_time` datetime DEFAULT NULL COMMENT '过期时间(可选)',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT 0 COMMENT '删除状态:0=未删除,1=已删除',
|
||||
`version` bigint NOT NULL DEFAULT 0 COMMENT '版本号(乐观锁)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_code` (`code`),
|
||||
KEY `idx_agent_id` (`agent_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_target_level` (`target_level`),
|
||||
KEY `idx_used_user_id` (`used_user_id`),
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代理邀请码表';
|
||||
|
||||
-- ============================================
|
||||
-- 表创建完成
|
||||
-- ============================================
|
||||
-- 执行完成后,请使用goctl生成新的Model代码
|
||||
-- 参考命令见 generate_agent_models.md 文件
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
# 生成新代理系统Model说明
|
||||
|
||||
## 一、执行数据库迁移
|
||||
|
||||
### 1.1 备份数据库(重要!)
|
||||
|
||||
```bash
|
||||
# 备份整个数据库
|
||||
mysqldump -u root -p your_database > backup_before_migration.sql
|
||||
|
||||
# 或者只备份agent相关表
|
||||
mysqldump -u root -p your_database agent agent_audit agent_closure agent_commission agent_wallet agent_link agent_order agent_rewards agent_membership_config agent_product_config agent_real_name agent_withdrawal agent_withdrawal_tax agent_withdrawal_tax_exemption agent_active_stat agent_platform_deduction agent_commission_deduction agent_membership_recharge_order agent_membership_user_config > agent_tables_backup.sql
|
||||
```
|
||||
|
||||
### 1.2 执行迁移SQL
|
||||
|
||||
```bash
|
||||
# 方式1:使用mysql命令行
|
||||
mysql -u root -p your_database < deploy/sql/agent_system_migration.sql
|
||||
|
||||
# 方式2:使用数据库客户端工具(如Navicat、DBeaver等)
|
||||
# 直接执行 deploy/sql/agent_system_migration.sql 文件
|
||||
```
|
||||
|
||||
## 二、生成Model代码
|
||||
|
||||
### 2.1 方式一:使用脚本批量生成(推荐)
|
||||
|
||||
**Windows (PowerShell):**
|
||||
|
||||
```powershell
|
||||
# 修改脚本中的数据库配置
|
||||
# 然后执行(需要安装goctl工具)
|
||||
cd deploy/sql
|
||||
bash generate_agent_models.sh
|
||||
```
|
||||
|
||||
**Linux/Mac:**
|
||||
|
||||
```bash
|
||||
# 修改脚本中的数据库配置
|
||||
chmod +x deploy/sql/generate_agent_models.sh
|
||||
cd deploy/sql
|
||||
./generate_agent_models.sh
|
||||
```
|
||||
|
||||
### 2.2 方式二:使用goctl命令逐个生成
|
||||
|
||||
```bash
|
||||
# 1. agent表
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 2. agent_audit表
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_audit" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 3. agent_wallet表
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_wallet" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 4. agent_relation表(新表,替代agent_closure)
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_relation" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 5. agent_link表
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_link" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 6. agent_order表
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_order" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 7. agent_commission表
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_commission" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 8. agent_rebate表(新表)
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_rebate" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 9. agent_upgrade表(新表)
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_upgrade" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 10. agent_withdrawal表
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_withdrawal" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 11. agent_config表(新表)
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_config" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 12. agent_product_config表
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_product_config" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 13. agent_real_name表
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_real_name" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
|
||||
# 14. agent_withdrawal_tax表
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent_withdrawal_tax" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
```
|
||||
|
||||
### 2.3 方式三:使用goctl一次性生成所有表
|
||||
|
||||
```bash
|
||||
# 生成所有agent相关表的Model
|
||||
goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" -table="agent,agent_audit,agent_wallet,agent_relation,agent_link,agent_order,agent_commission,agent_rebate,agent_upgrade,agent_withdrawal,agent_config,agent_product_config,agent_real_name,agent_withdrawal_tax" -dir="app/main/model" -cache=true --style=goZero --home="./deploy/template"
|
||||
```
|
||||
|
||||
## 三、表结构变化说明
|
||||
|
||||
### 3.1 删除的旧表
|
||||
|
||||
以下表在新系统中已删除,不再需要:
|
||||
|
||||
- `agent_closure` → 被 `agent_relation` 替代
|
||||
- `agent_commission_deduction` → 新系统不需要
|
||||
- `agent_rewards` → 新系统不需要
|
||||
- `agent_membership_config` → 新系统不需要
|
||||
- `agent_membership_recharge_order` → 新系统不需要
|
||||
- `agent_membership_user_config` → 新系统不需要
|
||||
- `agent_platform_deduction` → 新系统不需要
|
||||
- `agent_active_stat` → 新系统不需要
|
||||
- `agent_withdrawal_tax_exemption` → 新系统不需要
|
||||
|
||||
### 3.2 新增的表
|
||||
|
||||
- `agent_relation` - 替代 `agent_closure`,支持关系脱离
|
||||
- `agent_rebate` - 等级加成返佣记录表
|
||||
- `agent_upgrade` - 代理升级记录表
|
||||
- `agent_config` - 代理系统配置表
|
||||
|
||||
### 3.3 结构变化的表
|
||||
|
||||
以下表结构有变化,需要重新生成Model:
|
||||
|
||||
- `agent` - 等级字段改为数值型,新增 `team_leader_id`
|
||||
- `agent_audit` - 新增 `ancestor_mobile`、`audit_user_id` 字段
|
||||
- `agent_wallet` - 字段保持不变
|
||||
- `agent_link` - 新增唯一约束
|
||||
- `agent_order` - 新增 `order_amount`、`process_status` 等字段
|
||||
- `agent_commission` - 字段保持不变
|
||||
- `agent_product_config` - 字段保持不变
|
||||
- `agent_real_name` - 新增 `audit_user_id` 字段
|
||||
- `agent_withdrawal` - 字段保持不变
|
||||
- `agent_withdrawal_tax` - 字段保持不变
|
||||
|
||||
## 四、生成后的文件结构
|
||||
|
||||
生成后,会在 `app/main/model` 目录下创建以下文件:
|
||||
|
||||
```
|
||||
app/main/model/
|
||||
├── agentModel.go # agent表Model(自定义方法)
|
||||
├── agentModel_gen.go # agent表Model(自动生成)
|
||||
├── agentAuditModel.go # agent_audit表Model(自定义方法)
|
||||
├── agentAuditModel_gen.go # agent_audit表Model(自动生成)
|
||||
├── agentWalletModel.go # agent_wallet表Model(自定义方法)
|
||||
├── agentWalletModel_gen.go # agent_wallet表Model(自动生成)
|
||||
├── agentRelationModel.go # agent_relation表Model(自定义方法,新)
|
||||
├── agentRelationModel_gen.go # agent_relation表Model(自动生成,新)
|
||||
├── agentLinkModel.go # agent_link表Model(自定义方法)
|
||||
├── agentLinkModel_gen.go # agent_link表Model(自动生成)
|
||||
├── agentOrderModel.go # agent_order表Model(自定义方法)
|
||||
├── agentOrderModel_gen.go # agent_order表Model(自动生成)
|
||||
├── agentCommissionModel.go # agent_commission表Model(自定义方法)
|
||||
├── agentCommissionModel_gen.go # agent_commission表Model(自动生成)
|
||||
├── agentRebateModel.go # agent_rebate表Model(自定义方法,新)
|
||||
├── agentRebateModel_gen.go # agent_rebate表Model(自动生成,新)
|
||||
├── agentUpgradeModel.go # agent_upgrade表Model(自定义方法,新)
|
||||
├── agentUpgradeModel_gen.go # agent_upgrade表Model(自动生成,新)
|
||||
├── agentWithdrawalModel.go # agent_withdrawal表Model(自定义方法)
|
||||
├── agentWithdrawalModel_gen.go # agent_withdrawal表Model(自动生成)
|
||||
├── agentConfigModel.go # agent_config表Model(自定义方法,新)
|
||||
├── agentConfigModel_gen.go # agent_config表Model(自动生成,新)
|
||||
├── agentProductConfigModel.go # agent_product_config表Model(自定义方法)
|
||||
├── agentProductConfigModel_gen.go # agent_product_config表Model(自动生成)
|
||||
├── agentRealNameModel.go # agent_real_name表Model(自定义方法)
|
||||
├── agentRealNameModel_gen.go # agent_real_name表Model(自动生成)
|
||||
├── agentWithdrawalTaxModel.go # agent_withdrawal_tax表Model(自定义方法)
|
||||
└── agentWithdrawalTaxModel_gen.go # agent_withdrawal_tax表Model(自动生成)
|
||||
```
|
||||
|
||||
## 五、注意事项
|
||||
|
||||
1. **备份数据**:执行迁移前务必备份数据库
|
||||
2. **测试环境**:建议先在测试环境执行,验证无误后再在生产环境执行
|
||||
3. **代码兼容**:生成新Model后,需要更新相关业务代码以适配新的表结构
|
||||
4. **删除旧Model**:生成新Model后,需要删除旧的Model文件(如 `agentClosureModel.go`)
|
||||
5. **更新ServiceContext**:需要在 `svc/servicecontext.go` 中更新Model初始化代码
|
||||
|
||||
## 六、验证
|
||||
|
||||
生成Model后,请验证:
|
||||
|
||||
1. 所有新表都有对应的Model文件
|
||||
2. Model文件可以正常编译
|
||||
3. 数据库连接正常
|
||||
4. 基本的CRUD操作可以正常执行
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# 生成新代理系统Model脚本
|
||||
# 使用goctl工具根据数据库表生成Model代码
|
||||
# ============================================
|
||||
|
||||
# 数据库配置(请根据实际情况修改)
|
||||
DB_HOST="localhost"
|
||||
DB_PORT="3306"
|
||||
DB_USER="root"
|
||||
DB_PASSWORD="your_password"
|
||||
DB_NAME="your_database"
|
||||
|
||||
# Model输出目录
|
||||
MODEL_DIR="app/main/model"
|
||||
|
||||
# goctl模板目录
|
||||
TEMPLATE_DIR="./deploy/template"
|
||||
|
||||
echo "============================================"
|
||||
echo "开始生成新代理系统Model..."
|
||||
echo "============================================"
|
||||
|
||||
# 生成各个表的Model
|
||||
echo "1. 生成 agent Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "2. 生成 agent_audit Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_audit" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "3. 生成 agent_wallet Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_wallet" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "4. 生成 agent_relation Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_relation" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "5. 生成 agent_link Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_link" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "6. 生成 agent_order Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_order" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "7. 生成 agent_commission Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_commission" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "8. 生成 agent_rebate Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_rebate" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "9. 生成 agent_upgrade Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_upgrade" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "10. 生成 agent_withdrawal Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_withdrawal" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "11. 生成 agent_config Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_config" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "12. 生成 agent_product_config Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_product_config" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "13. 生成 agent_real_name Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_real_name" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "14. 生成 agent_withdrawal_tax Model..."
|
||||
goctl model mysql datasource -url="${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:${DB_PORT})/${DB_NAME}" -table="agent_withdrawal_tax" -dir="${MODEL_DIR}" -cache=true --style=goZero --home="${TEMPLATE_DIR}"
|
||||
|
||||
echo "============================================"
|
||||
echo "Model生成完成!"
|
||||
echo "============================================"
|
||||
|
||||
52
deploy/sql/query_user_record.sql
Normal file
52
deploy/sql/query_user_record.sql
Normal file
@@ -0,0 +1,52 @@
|
||||
-- 查询用户记录表
|
||||
-- 用途:记录用户查询时输入的姓名、身份证、手机号,以及支付订单号等,用于通过查询条件追溯订单信息
|
||||
-- 执行说明:在目标数据库执行此脚本创建表
|
||||
--
|
||||
-- 使用说明(需在业务代码中接入):
|
||||
-- 1. 用户提交查询时(queryservicelogic.CacheData 之后):INSERT 记录 name, id_card, mobile(已 AES-ECB+Base64 加密), product, query_no, user_id, agent_identifier
|
||||
-- 2. 用户发起支付并创建 order 时(paymentlogic.QueryOrderPayment 中 Insert order 之后):UPDATE 本表 SET order_id WHERE query_no=outTradeNo
|
||||
-- 3. 支付回调成功更新 order 后(alipaycallbacklogic/wechatpaycallbacklogic):UPDATE 本表 SET platform_order_id WHERE query_no=orderNo
|
||||
--
|
||||
-- 敏感字段加密:name、id_card、mobile 使用 pkg/lzkit/crypto 的 AES-ECB+Base64 加密后入库,密钥为 config.Encrypt.SecretKey(hex)。
|
||||
-- 解密:姓名 crypto.AesEcbDecrypt(rec.Name, key);身份证 crypto.DecryptIDCard(rec.IdCard, key);手机 crypto.DecryptMobile(rec.Mobile, secretKey)。
|
||||
--
|
||||
-- 查询功能说明:
|
||||
-- 后台订单列表页面可以通过被查询人的姓名、身份证号、手机号(三要素)筛选对应的订单
|
||||
-- 支持单独使用任一要素查询,也支持组合查询(AND 关系)
|
||||
-- 查询逻辑:先加密查询条件,然后在 query_user_record 表中查找匹配记录,再通过 order_id 或 query_no 关联到 order 表
|
||||
|
||||
CREATE TABLE `query_user_record` (
|
||||
`id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键ID(UUID)',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT '0' COMMENT '删除状态:0-未删除,1-已删除',
|
||||
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
|
||||
/* 业务字段 - 用户查询输入(name、id_card、mobile 为 AES-ECB+Base64 密文) */
|
||||
`user_id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID(UUID,关联 user 表)',
|
||||
`name` varchar(256) NOT NULL DEFAULT '' COMMENT '姓名密文(AES-ECB+Base64),用于按姓名筛选订单',
|
||||
`id_card` varchar(128) NOT NULL DEFAULT '' COMMENT '身份证号密文(AES-ECB+Base64),用于按身份证号筛选订单',
|
||||
`mobile` varchar(128) NOT NULL DEFAULT '' COMMENT '手机号密文(AES-ECB+Base64),用于按手机号筛选订单',
|
||||
`product` varchar(50) NOT NULL DEFAULT '' COMMENT '产品类型,如 marriage/homeservice/riskassessment 等',
|
||||
|
||||
/* 关联字段 - 查询单号与订单 */
|
||||
`query_no` varchar(64) NOT NULL DEFAULT '' COMMENT '查询单号(与 order.order_no 一致,如 Q_xxx),用户提交查询时生成',
|
||||
`order_id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '订单ID(UUID,关联 order 表),用户发起支付并创建订单后写入',
|
||||
`platform_order_id` varchar(64) DEFAULT NULL COMMENT '支付平台订单号(支付宝/微信),支付成功后由回调写入',
|
||||
`agent_identifier` varchar(255) DEFAULT NULL COMMENT '代理标识,代理渠道时有值',
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_user_id` (`user_id`) COMMENT '用于按用户ID查询',
|
||||
KEY `idx_name` (`name`) COMMENT '用于按姓名筛选订单(精确匹配加密后的密文)',
|
||||
KEY `idx_id_card` (`id_card`) COMMENT '用于按身份证号筛选订单(精确匹配加密后的密文)',
|
||||
KEY `idx_mobile` (`mobile`) COMMENT '用于按手机号筛选订单(精确匹配加密后的密文)',
|
||||
KEY `idx_query_no` (`query_no`) COMMENT '用于按查询单号查询',
|
||||
KEY `idx_order_id` (`order_id`) COMMENT '用于按订单ID查询',
|
||||
KEY `idx_platform_order_id` (`platform_order_id`) COMMENT '用于按支付平台订单号查询',
|
||||
KEY `idx_create_time` (`create_time`) COMMENT '用于按创建时间排序',
|
||||
KEY `idx_del_state_order_id` (`del_state`, `order_id`) COMMENT '用于筛选未删除记录并按订单ID查询',
|
||||
KEY `idx_del_state_query_no` (`del_state`, `query_no`) COMMENT '用于筛选未删除记录并按查询单号查询',
|
||||
KEY `idx_name_id_card_mobile` (`name`, `id_card`, `mobile`) COMMENT '复合索引:用于多条件组合筛选(姓名+身份证+手机号)',
|
||||
KEY `idx_del_state_name_id_card_mobile` (`del_state`, `name`, `id_card`, `mobile`) COMMENT '复合索引:用于筛选未删除记录并按三要素组合查询'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='查询用户记录表:姓名、身份证、手机号、支付订单号等,用于通过查询信息追溯订单。支持通过被查询人三要素(姓名、身份证、手机号)筛选订单' ROW_FORMAT=DYNAMIC;
|
||||
66
deploy/sql/query_user_record_add_indexes.sql
Normal file
66
deploy/sql/query_user_record_add_indexes.sql
Normal file
@@ -0,0 +1,66 @@
|
||||
-- ============================================================
|
||||
-- 查询用户记录表 - 添加索引脚本
|
||||
-- 用途:为已存在的 query_user_record 表添加索引,优化按姓名、身份证、手机号筛选订单的查询性能
|
||||
-- 执行说明:在目标数据库执行此脚本添加索引
|
||||
-- 注意:如果索引已存在,执行会报错,可以忽略或先检查索引是否存在
|
||||
-- ============================================================
|
||||
|
||||
-- 检查并添加 idx_name 索引(用于按姓名筛选订单)
|
||||
-- 如果索引已存在,此语句会报错,可以忽略
|
||||
ALTER TABLE `query_user_record`
|
||||
ADD INDEX `idx_name` (`name`) COMMENT '用于按姓名筛选订单';
|
||||
|
||||
-- 检查并添加 idx_del_state_order_id 复合索引(用于筛选未删除记录并按订单ID查询)
|
||||
-- 如果索引已存在,此语句会报错,可以忽略
|
||||
ALTER TABLE `query_user_record`
|
||||
ADD INDEX `idx_del_state_order_id` (`del_state`, `order_id`) COMMENT '用于筛选未删除记录并按订单ID查询';
|
||||
|
||||
-- 检查并添加 idx_name_id_card_mobile 复合索引(用于多条件组合筛选)
|
||||
-- 如果索引已存在,此语句会报错,可以忽略
|
||||
ALTER TABLE `query_user_record`
|
||||
ADD INDEX `idx_name_id_card_mobile` (`name`, `id_card`, `mobile`) COMMENT '复合索引:用于多条件组合筛选(姓名+身份证+手机号)';
|
||||
|
||||
-- ============================================================
|
||||
-- 可选:如果希望先检查索引是否存在再添加,可以使用以下存储过程方式
|
||||
-- ============================================================
|
||||
--
|
||||
-- DELIMITER $$
|
||||
--
|
||||
-- DROP PROCEDURE IF EXISTS `add_index_if_not_exists`$$
|
||||
--
|
||||
-- CREATE PROCEDURE `add_index_if_not_exists`(
|
||||
-- IN p_table_name VARCHAR(128),
|
||||
-- IN p_index_name VARCHAR(128),
|
||||
-- IN p_index_definition TEXT
|
||||
-- )
|
||||
-- BEGIN
|
||||
-- DECLARE v_index_exists INT DEFAULT 0;
|
||||
--
|
||||
-- SELECT COUNT(*) INTO v_index_exists
|
||||
-- FROM information_schema.statistics
|
||||
-- WHERE table_schema = DATABASE()
|
||||
-- AND table_name = p_table_name
|
||||
-- AND index_name = p_index_name;
|
||||
--
|
||||
-- IF v_index_exists = 0 THEN
|
||||
-- SET @sql = CONCAT('ALTER TABLE `', p_table_name, '` ADD INDEX `', p_index_name, '` ', p_index_definition);
|
||||
-- PREPARE stmt FROM @sql;
|
||||
-- EXECUTE stmt;
|
||||
-- DEALLOCATE PREPARE stmt;
|
||||
-- SELECT CONCAT('索引 ', p_index_name, ' 已成功添加') AS result;
|
||||
-- ELSE
|
||||
-- SELECT CONCAT('索引 ', p_index_name, ' 已存在,跳过') AS result;
|
||||
-- END IF;
|
||||
-- END$$
|
||||
--
|
||||
-- DELIMITER ;
|
||||
--
|
||||
-- -- 使用存储过程添加索引
|
||||
-- CALL `add_index_if_not_exists`('query_user_record', 'idx_name', '(`name`) COMMENT ''用于按姓名筛选订单''');
|
||||
-- CALL `add_index_if_not_exists`('query_user_record', 'idx_del_state_order_id', '(`del_state`, `order_id`) COMMENT ''用于筛选未删除记录并按订单ID查询''');
|
||||
-- CALL `add_index_if_not_exists`('query_user_record', 'idx_name_id_card_mobile', '(`name`, `id_card`, `mobile`) COMMENT ''复合索引:用于多条件组合筛选(姓名+身份证+手机号)''');
|
||||
--
|
||||
-- -- 清理存储过程
|
||||
-- DROP PROCEDURE IF EXISTS `add_index_if_not_exists`;
|
||||
--
|
||||
-- ============================================================
|
||||
@@ -1,25 +0,0 @@
|
||||
-- ============================================
|
||||
-- 移除代理产品配置表中的 product_name 字段
|
||||
-- 说明:改为通过 product_id 关联查询 product 表获取产品名称
|
||||
-- 执行时间:2025-01-XX
|
||||
-- ============================================
|
||||
|
||||
-- 删除 product_name 字段
|
||||
ALTER TABLE `agent_product_config` DROP COLUMN `product_name`;
|
||||
|
||||
-- ============================================
|
||||
-- 验证查询:检查字段是否已删除
|
||||
-- ============================================
|
||||
DESC `agent_product_config`;
|
||||
|
||||
-- ============================================
|
||||
-- 验证查询:通过关联查询获取产品名称
|
||||
-- ============================================
|
||||
SELECT apc.id, apc.product_id, p.product_name, apc.base_price, apc.system_max_price, apc.price_threshold, apc.price_fee_rate, apc.del_state, apc.create_time
|
||||
FROM
|
||||
`agent_product_config` apc
|
||||
LEFT JOIN `product` p ON apc.product_id = p.id
|
||||
AND p.del_state = 0
|
||||
WHERE
|
||||
apc.del_state = 0
|
||||
ORDER BY apc.product_id;
|
||||
@@ -1,112 +0,0 @@
|
||||
-- ============================================
|
||||
-- 同步产品表数据到代理产品配置表
|
||||
-- 说明:为现有产品创建对应的代理产品配置记录
|
||||
-- 执行时间:2025-01-XX
|
||||
-- ============================================
|
||||
|
||||
-- 方式1:使用 INSERT IGNORE(如果记录已存在则忽略)
|
||||
-- 注意:product_name 字段已移除,改为通过 product_id 关联查询 product 表获取
|
||||
INSERT IGNORE INTO
|
||||
`agent_product_config` (
|
||||
`product_id`,
|
||||
`base_price`,
|
||||
`system_max_price`,
|
||||
`price_threshold`,
|
||||
`price_fee_rate`,
|
||||
`del_state`,
|
||||
`version`
|
||||
)
|
||||
VALUES (
|
||||
1,
|
||||
0.00,
|
||||
9999.99,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
0
|
||||
),
|
||||
(
|
||||
2,
|
||||
0.00,
|
||||
9999.99,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
0
|
||||
),
|
||||
(
|
||||
3,
|
||||
0.00,
|
||||
9999.99,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
0
|
||||
),
|
||||
(
|
||||
4,
|
||||
0.00,
|
||||
9999.99,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
0
|
||||
),
|
||||
(
|
||||
5,
|
||||
0.00,
|
||||
9999.99,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
0
|
||||
),
|
||||
(
|
||||
6,
|
||||
0.00,
|
||||
9999.99,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
0
|
||||
),
|
||||
(
|
||||
7,
|
||||
0.00,
|
||||
9999.99,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- 方式2:使用 INSERT ... ON DUPLICATE KEY UPDATE(如果记录已存在则忽略)
|
||||
-- 注意:product_name 字段已移除,不再需要更新
|
||||
-- ============================================
|
||||
/*
|
||||
INSERT INTO `agent_product_config`
|
||||
(`product_id`, `base_price`, `system_max_price`, `price_threshold`, `price_fee_rate`, `del_state`, `version`)
|
||||
VALUES
|
||||
(1, 0.00, 9999.99, NULL, NULL, 0, 0),
|
||||
(2, 0.00, 9999.99, NULL, NULL, 0, 0),
|
||||
(3, 0.00, 9999.99, NULL, NULL, 0, 0),
|
||||
(4, 0.00, 9999.99, NULL, NULL, 0, 0),
|
||||
(5, 0.00, 9999.99, NULL, NULL, 0, 0),
|
||||
(6, 0.00, 9999.99, NULL, NULL, 0, 0),
|
||||
(7, 0.00, 9999.99, NULL, NULL, 0, 0)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`product_id` = VALUES(`product_id`);
|
||||
*/
|
||||
|
||||
-- ============================================
|
||||
-- 验证查询:检查同步结果
|
||||
-- ============================================
|
||||
SELECT apc.id, apc.product_id, p.product_name, apc.base_price, apc.system_max_price, apc.price_threshold, apc.price_fee_rate, apc.del_state, apc.create_time
|
||||
FROM
|
||||
`agent_product_config` apc
|
||||
LEFT JOIN `product` p ON apc.product_id = p.id
|
||||
AND p.del_state = 0
|
||||
WHERE
|
||||
apc.del_state = 0
|
||||
ORDER BY apc.product_id;
|
||||
@@ -1,62 +0,0 @@
|
||||
-- ============================================
|
||||
-- 表结构模板(UUID版本)
|
||||
-- ============================================
|
||||
-- 注意:系统已迁移到UUID主键,新表请使用此模板
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE `表名` (
|
||||
`id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT '0',
|
||||
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
|
||||
/* 业务字段开始 */
|
||||
|
||||
`字段1` 数据类型 [约束条件] [DEFAULT 默认值] [COMMENT '字段说明'],
|
||||
`字段2` 数据类型 [约束条件] [DEFAULT 默认值] [COMMENT '字段说明'],
|
||||
/* 关联字段 - 软关联(使用UUID) */
|
||||
`关联表id` CHAR(36) [NOT NULL] [DEFAULT NULL] COMMENT '关联到XX表的UUID',
|
||||
/* 业务字段结束 */
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
/* 索引定义 */
|
||||
UNIQUE KEY `索引名称` (`字段名`),
|
||||
KEY `idx_关联字段` (`关联表id`) COMMENT '优化关联查询'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='表说明';
|
||||
|
||||
-- ============================================
|
||||
-- UUID生成说明
|
||||
-- ============================================
|
||||
-- 1. 应用层生成:使用Go的uuid.NewString()生成UUID
|
||||
-- 示例:id := uuid.NewString()
|
||||
-- 2. 数据库层生成:使用MySQL的UUID()函数(不推荐,性能较差)
|
||||
-- 示例:INSERT INTO table (id, ...) VALUES (UUID(), ...)
|
||||
-- 3. 推荐方式:在应用层生成UUID,然后插入数据库
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- 旧版本模板(bigint主键,已废弃)
|
||||
-- ============================================
|
||||
-- CREATE TABLE `表名` (
|
||||
-- `id` bigint NOT NULL AUTO_INCREMENT,
|
||||
-- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
-- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
-- `delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
-- `del_state` tinyint NOT NULL DEFAULT '0',
|
||||
-- `version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
--
|
||||
-- /* 业务字段开始 */
|
||||
-- `字段1` 数据类型 [约束条件] [DEFAULT 默认值] [COMMENT '字段说明'],
|
||||
-- `字段2` 数据类型 [约束条件] [DEFAULT 默认值] [COMMENT '字段说明'],
|
||||
-- /* 关联字段 - 软关联 */
|
||||
-- `关联表id` bigint [NOT NULL] [DEFAULT '0'] COMMENT '关联到XX表的id',
|
||||
-- /* 业务字段结束 */
|
||||
--
|
||||
-- PRIMARY KEY (`id`),
|
||||
-- /* 索引定义 */
|
||||
-- UNIQUE KEY `索引名称` (`字段名`),
|
||||
-- KEY `idx_关联字段` (`关联表id`) COMMENT '优化关联查询'
|
||||
-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='表说明';
|
||||
-- ============================================
|
||||
@@ -1,48 +0,0 @@
|
||||
-- ============================================
|
||||
-- 用户系统重构SQL脚本(简化版 - 开发环境)
|
||||
-- 根据《用户系统重构计划书》执行
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- 第一部分:UserAuth表唯一索引
|
||||
-- ============================================
|
||||
|
||||
-- 确保user_auth表的(auth_type, auth_key)唯一性
|
||||
-- 如果已存在同名索引,先删除
|
||||
ALTER TABLE `user_auth` DROP INDEX IF EXISTS `uk_auth_type_key`;
|
||||
|
||||
-- 添加唯一索引
|
||||
ALTER TABLE `user_auth`
|
||||
ADD UNIQUE INDEX `uk_auth_type_key` (`auth_type`, `auth_key`) COMMENT '确保同一个认证方式只能绑定一个用户';
|
||||
|
||||
-- 允许一个用户绑定同一认证类型的多个记录(例如多个UUID)
|
||||
ALTER TABLE `user_auth` DROP INDEX IF EXISTS `unique_userId_key`;
|
||||
ALTER TABLE `user_auth` ADD INDEX `idx_user_id_auth_type` (`user_id`, `auth_type`);
|
||||
|
||||
-- ============================================
|
||||
-- 第二部分:删除UserTemp表(开发环境直接删除)
|
||||
-- ============================================
|
||||
|
||||
-- 注意:开发环境没有数据,直接删除表即可
|
||||
-- 如果表不存在,会报错但可以忽略
|
||||
|
||||
DROP TABLE IF EXISTS `user_temp`;
|
||||
|
||||
-- ============================================
|
||||
-- 第三部分:索引优化(可选)
|
||||
-- ============================================
|
||||
|
||||
-- 为user_auth表添加user_id和auth_type的联合索引(如果查询频繁)
|
||||
-- ALTER TABLE `user_auth`
|
||||
-- ADD INDEX `idx_user_id_auth_type` (`user_id`, `auth_type`) COMMENT '优化按用户查询认证类型';
|
||||
|
||||
-- 为user表添加mobile的唯一索引(如果不存在)
|
||||
-- ALTER TABLE `user`
|
||||
-- ADD UNIQUE INDEX `uk_mobile` (`mobile`) COMMENT '确保手机号唯一性';
|
||||
|
||||
-- ============================================
|
||||
-- 注意事项
|
||||
-- ============================================
|
||||
-- 1. 此脚本适用于开发环境,直接删除UserTemp表
|
||||
-- 2. 如果生产环境有数据,请使用完整版迁移脚本
|
||||
-- 3. 执行后,确保代码中已移除所有对UserTemp表的引用
|
||||
@@ -1,45 +0,0 @@
|
||||
-- 将 user_temp 迁移为 user(mobile 为空)与 user_auth
|
||||
|
||||
START TRANSACTION;
|
||||
|
||||
-- 1) 迁移临时用户到 user(mobile 置为空)
|
||||
INSERT INTO
|
||||
`user` (
|
||||
`delete_time`,
|
||||
`del_state`,
|
||||
`version`,
|
||||
`mobile`,
|
||||
`password`,
|
||||
`nickname`,
|
||||
`info`,
|
||||
`inside`
|
||||
)
|
||||
SELECT NULL, 0, COALESCE(ut.version, 0), NULL, NULL, NULL, '', 0
|
||||
FROM `user_temp` ut
|
||||
WHERE
|
||||
ut.del_state = 0;
|
||||
|
||||
-- 2) 将临时认证迁移到 user_auth(按插入顺序关联最近插入的 user.id)
|
||||
INSERT INTO
|
||||
`user_auth` (
|
||||
`delete_time`,
|
||||
`del_state`,
|
||||
`version`,
|
||||
`user_id`,
|
||||
`auth_key`,
|
||||
`auth_type`
|
||||
)
|
||||
SELECT NULL, 0, COALESCE(ut.version, 0), u.id, ut.auth_key, ut.auth_type
|
||||
FROM `user_temp` ut
|
||||
JOIN `user` u ON u.del_state = 0
|
||||
AND u.mobile IS NULL
|
||||
WHERE
|
||||
ut.del_state = 0;
|
||||
|
||||
-- 注意:以上为示意,实际生产应通过显式映射(如临时ID与新UserID映射表)确保一一对应,避免笛卡尔匹配。
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- 唯一索引保障
|
||||
ALTER TABLE `user_auth`
|
||||
ADD UNIQUE INDEX `idx_auth_type_key` (`auth_type`, `auth_key`);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,446 +0,0 @@
|
||||
-- ============================================
|
||||
-- UUID迁移脚本(简化版 - 开发环境)
|
||||
-- 将系统中所有bigint类型的ID字段改为CHAR(36)类型的UUID
|
||||
-- 注意:此脚本直接修改表结构,不保留旧数据,适用于开发环境
|
||||
-- ============================================
|
||||
|
||||
-- 注意:user表和product表已经是CHAR(36)类型,跳过修改
|
||||
|
||||
-- ============================================
|
||||
-- 第一部分:修改核心业务表
|
||||
-- ============================================
|
||||
|
||||
-- user表:已经是CHAR(36),跳过
|
||||
-- product表:已经是CHAR(36),跳过
|
||||
|
||||
-- order表:修改主键和外键字段
|
||||
ALTER TABLE `order`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL COMMENT '用户UUID',
|
||||
MODIFY COLUMN `product_id` CHAR(36) NOT NULL COMMENT '产品UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_user_id` (`user_id`),
|
||||
ADD INDEX `idx_product_id` (`product_id`);
|
||||
|
||||
-- query表:修改主键和外键字段
|
||||
ALTER TABLE `query`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `order_id` CHAR(36) NOT NULL COMMENT '订单UUID',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL COMMENT '用户UUID',
|
||||
MODIFY COLUMN `product_id` CHAR(36) NOT NULL COMMENT '产品UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_order_id` (`order_id`),
|
||||
ADD INDEX `idx_user_id` (`user_id`),
|
||||
ADD INDEX `idx_product_id` (`product_id`);
|
||||
|
||||
-- user_auth表:修改主键和外键字段(注意:user_id需要关联到user表的UUID)
|
||||
ALTER TABLE `user_auth`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL COMMENT '用户UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_user_id` (`user_id`),
|
||||
ADD UNIQUE INDEX `uk_auth_type_key` (`auth_type`, `auth_key`) COMMENT '确保同一个认证方式只能绑定一个用户';
|
||||
|
||||
-- agent表:修改主键和外键字段
|
||||
ALTER TABLE `agent`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL COMMENT '用户UUID',
|
||||
MODIFY COLUMN `team_leader_id` CHAR(36) NULL COMMENT '团队首领UUID',
|
||||
MODIFY COLUMN `invite_code_id` CHAR(36) NULL COMMENT '邀请码UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE INDEX `uk_user_id` (`user_id`),
|
||||
ADD INDEX `idx_team_leader_id` (`team_leader_id`),
|
||||
ADD INDEX `idx_invite_code_id` (`invite_code_id`);
|
||||
|
||||
-- ============================================
|
||||
-- 第二部分:修改代理相关表
|
||||
-- ============================================
|
||||
|
||||
-- agent_commission表
|
||||
ALTER TABLE `agent_commission`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
MODIFY COLUMN `order_id` CHAR(36) NOT NULL COMMENT '订单UUID',
|
||||
MODIFY COLUMN `product_id` CHAR(36) NOT NULL COMMENT '产品UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`),
|
||||
ADD INDEX `idx_order_id` (`order_id`),
|
||||
ADD INDEX `idx_product_id` (`product_id`);
|
||||
|
||||
-- agent_invite_code表
|
||||
ALTER TABLE `agent_invite_code`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NULL COMMENT '代理UUID',
|
||||
MODIFY COLUMN `used_user_id` CHAR(36) NULL COMMENT '使用用户UUID',
|
||||
MODIFY COLUMN `used_agent_id` CHAR(36) NULL COMMENT '使用代理UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`),
|
||||
ADD INDEX `idx_used_user_id` (`used_user_id`),
|
||||
ADD INDEX `idx_used_agent_id` (`used_agent_id`);
|
||||
|
||||
-- agent_link表
|
||||
ALTER TABLE `agent_link`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL COMMENT '用户UUID',
|
||||
MODIFY COLUMN `product_id` CHAR(36) NOT NULL COMMENT '产品UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`),
|
||||
ADD INDEX `idx_user_id` (`user_id`),
|
||||
ADD INDEX `idx_product_id` (`product_id`);
|
||||
|
||||
-- agent_order表
|
||||
ALTER TABLE `agent_order`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
MODIFY COLUMN `order_id` CHAR(36) NOT NULL COMMENT '订单UUID',
|
||||
MODIFY COLUMN `product_id` CHAR(36) NOT NULL COMMENT '产品UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`),
|
||||
ADD INDEX `idx_order_id` (`order_id`),
|
||||
ADD INDEX `idx_product_id` (`product_id`);
|
||||
|
||||
-- agent_wallet表
|
||||
ALTER TABLE `agent_wallet`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE INDEX `uk_agent_id` (`agent_id`);
|
||||
|
||||
-- agent_withdrawal表
|
||||
ALTER TABLE `agent_withdrawal`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
MODIFY COLUMN `wallet_id` CHAR(36) NOT NULL COMMENT '钱包UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`),
|
||||
ADD INDEX `idx_wallet_id` (`wallet_id`);
|
||||
|
||||
-- agent_withdrawal_tax表
|
||||
ALTER TABLE `agent_withdrawal_tax`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `withdrawal_id` CHAR(36) NOT NULL COMMENT '提现UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_withdrawal_id` (`withdrawal_id`);
|
||||
|
||||
-- agent_rebate表
|
||||
ALTER TABLE `agent_rebate`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
MODIFY COLUMN `order_id` CHAR(36) NOT NULL COMMENT '订单UUID',
|
||||
MODIFY COLUMN `product_id` CHAR(36) NOT NULL COMMENT '产品UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`),
|
||||
ADD INDEX `idx_order_id` (`order_id`),
|
||||
ADD INDEX `idx_product_id` (`product_id`);
|
||||
|
||||
-- agent_relation表
|
||||
ALTER TABLE `agent_relation`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
MODIFY COLUMN `parent_agent_id` CHAR(36) NULL COMMENT '上级代理UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`),
|
||||
ADD INDEX `idx_parent_agent_id` (`parent_agent_id`);
|
||||
|
||||
-- agent_upgrade表
|
||||
ALTER TABLE `agent_upgrade`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`);
|
||||
|
||||
-- agent_real_name表
|
||||
ALTER TABLE `agent_real_name`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`);
|
||||
|
||||
-- agent_config表
|
||||
ALTER TABLE `agent_config`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
-- agent_product_config表
|
||||
ALTER TABLE `agent_product_config`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `product_id` CHAR(36) NOT NULL COMMENT '产品UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_product_id` (`product_id`);
|
||||
|
||||
-- agent_short_link表
|
||||
ALTER TABLE `agent_short_link`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `invite_code_id` CHAR(36) NULL COMMENT '邀请码UUID',
|
||||
MODIFY COLUMN `link_id` CHAR(36) NULL COMMENT '链接UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_invite_code_id` (`invite_code_id`),
|
||||
ADD INDEX `idx_link_id` (`link_id`);
|
||||
|
||||
-- agent_invite_code_usage表
|
||||
ALTER TABLE `agent_invite_code_usage`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `invite_code_id` CHAR(36) NOT NULL COMMENT '邀请码UUID',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL COMMENT '用户UUID',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_invite_code_id` (`invite_code_id`),
|
||||
ADD INDEX `idx_user_id` (`user_id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`);
|
||||
|
||||
-- agent_freeze_task表
|
||||
ALTER TABLE `agent_freeze_task`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `agent_id` CHAR(36) NOT NULL COMMENT '代理UUID',
|
||||
MODIFY COLUMN `order_id` CHAR(36) NOT NULL COMMENT '订单UUID',
|
||||
MODIFY COLUMN `commission_id` CHAR(36) NOT NULL COMMENT '佣金UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_agent_id` (`agent_id`),
|
||||
ADD INDEX `idx_order_id` (`order_id`),
|
||||
ADD INDEX `idx_commission_id` (`commission_id`);
|
||||
|
||||
-- ============================================
|
||||
-- 第三部分:修改订单相关表
|
||||
-- ============================================
|
||||
|
||||
-- order_refund表
|
||||
ALTER TABLE `order_refund`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `order_id` CHAR(36) NOT NULL COMMENT '订单UUID',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL COMMENT '用户UUID',
|
||||
MODIFY COLUMN `product_id` CHAR(36) NOT NULL COMMENT '产品UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_order_id` (`order_id`),
|
||||
ADD INDEX `idx_user_id` (`user_id`),
|
||||
ADD INDEX `idx_product_id` (`product_id`);
|
||||
|
||||
-- ============================================
|
||||
-- 第四部分:修改查询相关表
|
||||
-- ============================================
|
||||
|
||||
-- query_cleanup_log表
|
||||
ALTER TABLE `query_cleanup_log`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
-- query_cleanup_detail表
|
||||
ALTER TABLE `query_cleanup_detail`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `cleanup_log_id` CHAR(36) NOT NULL COMMENT '清理日志UUID',
|
||||
MODIFY COLUMN `query_id` CHAR(36) NOT NULL COMMENT '查询UUID',
|
||||
MODIFY COLUMN `order_id` CHAR(36) NOT NULL COMMENT '订单UUID',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL COMMENT '用户UUID',
|
||||
MODIFY COLUMN `product_id` CHAR(36) NOT NULL COMMENT '产品UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_cleanup_log_id` (`cleanup_log_id`),
|
||||
ADD INDEX `idx_query_id` (`query_id`),
|
||||
ADD INDEX `idx_order_id` (`order_id`),
|
||||
ADD INDEX `idx_user_id` (`user_id`),
|
||||
ADD INDEX `idx_product_id` (`product_id`);
|
||||
|
||||
-- query_cleanup_config表
|
||||
ALTER TABLE `query_cleanup_config`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
-- ============================================
|
||||
-- 第五部分:修改产品相关表
|
||||
-- ============================================
|
||||
|
||||
-- product_feature表
|
||||
ALTER TABLE `product_feature`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `product_id` CHAR(36) NOT NULL COMMENT '产品UUID',
|
||||
MODIFY COLUMN `feature_id` CHAR(36) NOT NULL COMMENT '功能UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_product_id` (`product_id`),
|
||||
ADD INDEX `idx_feature_id` (`feature_id`);
|
||||
|
||||
-- feature表
|
||||
ALTER TABLE `feature`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
-- ============================================
|
||||
-- 第六部分:修改其他表
|
||||
-- ============================================
|
||||
|
||||
-- authorization_document表
|
||||
ALTER TABLE `authorization_document`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL COMMENT '用户UUID',
|
||||
MODIFY COLUMN `order_id` CHAR(36) NOT NULL COMMENT '订单UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_user_id` (`user_id`),
|
||||
ADD INDEX `idx_order_id` (`order_id`);
|
||||
|
||||
-- global_notifications表(注意:原表id是int类型)
|
||||
ALTER TABLE `global_notifications`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
-- example表
|
||||
ALTER TABLE `example`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `feature_id` CHAR(36) NOT NULL COMMENT '功能UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_feature_id` (`feature_id`);
|
||||
|
||||
-- ============================================
|
||||
-- 第七部分:修改管理后台表
|
||||
-- ============================================
|
||||
|
||||
-- admin_user表
|
||||
ALTER TABLE `admin_user`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
-- admin_role表
|
||||
ALTER TABLE `admin_role`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
-- admin_menu表(注意:字段名是pid,不是parent_id)
|
||||
ALTER TABLE `admin_menu`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `pid` CHAR(36) NOT NULL DEFAULT '0' COMMENT '父菜单UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_pid` (`pid`);
|
||||
|
||||
-- admin_api表
|
||||
ALTER TABLE `admin_api`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
-- admin_dict_type表
|
||||
ALTER TABLE `admin_dict_type`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
-- admin_dict_data表(注意:没有dict_type_id字段,使用dict_type字符串)
|
||||
-- 此表不需要修改外键字段,因为使用的是dict_type字符串而不是ID
|
||||
|
||||
ALTER TABLE `admin_dict_data`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
-- admin_user_role表
|
||||
ALTER TABLE `admin_user_role`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL DEFAULT '0' COMMENT '用户UUID',
|
||||
MODIFY COLUMN `role_id` CHAR(36) NOT NULL DEFAULT '0' COMMENT '角色UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_user_id` (`user_id`),
|
||||
ADD INDEX `idx_role_id` (`role_id`);
|
||||
|
||||
-- admin_role_menu表
|
||||
ALTER TABLE `admin_role_menu`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `role_id` CHAR(36) NOT NULL DEFAULT '0' COMMENT '角色UUID',
|
||||
MODIFY COLUMN `menu_id` CHAR(36) NOT NULL DEFAULT '0' COMMENT '菜单UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_role_id` (`role_id`),
|
||||
ADD INDEX `idx_menu_id` (`menu_id`);
|
||||
|
||||
-- admin_role_api表
|
||||
ALTER TABLE `admin_role_api`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `role_id` CHAR(36) NOT NULL DEFAULT '0' COMMENT '角色UUID',
|
||||
MODIFY COLUMN `api_id` CHAR(36) NOT NULL DEFAULT '0' COMMENT 'API UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_role_id` (`role_id`),
|
||||
ADD INDEX `idx_api_id` (`api_id`);
|
||||
|
||||
-- ============================================
|
||||
-- 第八部分:修改推广相关表(新增)
|
||||
-- ============================================
|
||||
|
||||
-- admin_promotion_link表
|
||||
ALTER TABLE `admin_promotion_link`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `admin_user_id` CHAR(36) NOT NULL DEFAULT '0' COMMENT '推广者账号UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_admin_user_id` (`admin_user_id`);
|
||||
|
||||
-- admin_promotion_link_stats_history表
|
||||
ALTER TABLE `admin_promotion_link_stats_history`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `link_id` CHAR(36) NOT NULL COMMENT '推广链接UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_link_id` (`link_id`);
|
||||
|
||||
-- admin_promotion_link_stats_total表
|
||||
ALTER TABLE `admin_promotion_link_stats_total`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `link_id` CHAR(36) NOT NULL COMMENT '推广链接UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `idx_link_id` (`link_id`);
|
||||
|
||||
-- admin_promotion_order表
|
||||
ALTER TABLE `admin_promotion_order`
|
||||
MODIFY COLUMN `id` CHAR(36) NOT NULL COMMENT 'UUID主键',
|
||||
MODIFY COLUMN `link_id` CHAR(36) NOT NULL COMMENT '推广链接UUID',
|
||||
MODIFY COLUMN `order_id` CHAR(36) NOT NULL COMMENT '订单UUID',
|
||||
MODIFY COLUMN `user_id` CHAR(36) NOT NULL COMMENT '下单用户UUID',
|
||||
MODIFY COLUMN `admin_user_id` CHAR(36) NOT NULL COMMENT '推广者账号UUID',
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE INDEX `uk_order_id` (`order_id`) COMMENT '确保每个订单只有一条推广记录',
|
||||
ADD INDEX `idx_link_id` (`link_id`),
|
||||
ADD INDEX `idx_user_id` (`user_id`) COMMENT '优化用户查询',
|
||||
ADD INDEX `idx_admin_user_id` (`admin_user_id`);
|
||||
|
||||
-- ============================================
|
||||
-- 注意事项
|
||||
-- ============================================
|
||||
-- 1. 此脚本直接修改表结构,不保留旧数据
|
||||
-- 2. user表和product表已经是CHAR(36)类型,已跳过
|
||||
-- 3. 执行后,所有ID字段都是CHAR(36)类型
|
||||
-- 4. 插入新记录时,需要在应用层生成UUID(使用uuid.NewString())
|
||||
-- 5. 如果表中有数据,需要先清空数据或手动填充UUID
|
||||
-- 6. 建议在开发环境先测试,确认无误后再应用到生产环境
|
||||
-- 7. admin_menu表使用pid字段而不是parent_id
|
||||
-- 8. admin_dict_data表使用dict_type字符串字段,不是dict_type_id
|
||||
-- 9. global_notifications表的id原为int类型,现改为CHAR(36)
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0.34
|
||||
|
||||
Reference in New Issue
Block a user