package models

import (
	"errors"
	"fmt"
	"mtp2_if/db"
	"mtp2_if/utils"
	"strconv"
	"time"
)

// Funcmenulist 功能菜单表
type Funcmenulist struct {
	Resourcecode  string `json:"resourcecode"  xorm:"'RESOURCECODE'" binding:"required"` // 资源代码
	Resourcename  string `json:"resourcename"  xorm:"'RESOURCENAME'"`                    // 资源名
	Resourcelevel uint32 `json:"resourcelevel"  xorm:"'RESOURCELEVEL'"`                  // 级别 1-一级 2-二级 3-三级 4-四级
	Menutype      uint32 `json:"menutype"  xorm:"'MENUTYPE'"`                            // 菜单类型 1-管理端 2-交易端 3-终端权限
	Parentcode    string `json:"parentcode"  xorm:"'PARENTCODE'"`                        // 上级资源代码
	URL           string `json:"url"  xorm:"'URL'"`                                      // URL
	Sort          uint32 `json:"sort"  xorm:"'SORT'"`                                    // 排序 - 一级时，是所有一级菜单的排序顺序；二级时是所属同一个一级菜单下的排序、三级时是同一个二级菜单下的排序
	Iconame       string `json:"iconame"  xorm:"'ICONAME'"`                              // 菜单图标
	Remark        string `json:"remark"  xorm:"'REMARK'"`                                // 备注
}

// TableName is FUNCMENULIST
func (Funcmenulist) TableName() string {
	return "FUNCMENULIST"
}

// GetPCMenuList 获取PC版本功能菜单表
func (t *Funcmenulist) GetPCMenuList(resourceCode, parentCode string) ([]Funcmenulist, error) {
	funcMenuLists := make([]Funcmenulist, 0)
	session := db.GetEngine().Table("FUNCMENULIST T").Where("T.MENUTYPE = 2")
	if len(resourceCode) != 0 {
		session = session.And("T.RESOURCECODE = ?", resourceCode)
	}
	if len(parentCode) != 0 {
		session = session.And("T.PARENTCODE = ?", parentCode)
	}
	if err := session.Find(&funcMenuLists); err != nil {
		return nil, err
	}

	return funcMenuLists, nil
}

// Rolefuncmenu 角色菜单表
type Rolefuncmenu struct {
	Roleid       uint32 `json:"roleid"  xorm:"'ROLEID'" binding:"required"`             // 角色权限ID
	Resourcecode string `json:"resourcecode"  xorm:"'RESOURCECODE'" binding:"required"` // 菜单代码
}

// TableName is ROLEFUNCMENU
func (Rolefuncmenu) TableName() string {
	return "ROLEFUNCMENU"
}

// Tabledefine 列表定义表
type Tabledefine struct {
	Tablekey  string `json:"tablekey"  xorm:"'TABLEKEY'" binding:"required"` // 列表Key
	Tabletype uint32 `json:"tabletype"  xorm:"'TABLETYPE'"`                  // 列表类型 - 1:管理端 2:终端
	Tablename string `json:"tablename"  xorm:"'TABLENAME'"`                  // 列表名称
	Tabelmenu string `json:"tabelmenu"  xorm:"'TABELMENU'"`                  // 列表菜单
	Remark    string `json:"remark"  xorm:"'REMARK'"`                        // Remark
}

// TableName is TABLEDEFINE
func (Tabledefine) TableName() string {
	return "TABLEDEFINE"
}

// Tablecolumnconfig 列表表头配置表
type Tablecolumnconfig struct {
	Autoid          uint64 `json:"autoid"  xorm:"'AUTOID'" binding:"required"` // AutoID
	Tablekey        string `json:"tablekey"  xorm:"'TABLEKEY'"`                // 列表Key
	Columnfield     string `json:"columnfield"  xorm:"'COLUMNFIELD'"`          // 列字段
	Columntitle     string `json:"columntitle"  xorm:"'COLUMNTITLE'"`          // 列Title
	Columnwidth     string `json:"columnwidth"  xorm:"'COLUMNWIDTH'"`          // 列宽
	Orderindex      uint32 `json:"orderindex"  xorm:"'ORDERINDEX'"`            // 顺序
	Isshow          uint32 `json:"isshow"  xorm:"'ISSHOW'"`                    // 是否显示 - 0:不显示 1:显示
	Aligntype       uint32 `json:"aligntype"  xorm:"'ALIGNTYPE'"`              // 对齐方式 - 1:居中对齐 2:左对齐 3:右对齐
	Formattertype   string `json:"formattertype"  xorm:"'FORMATTERTYPE'"`      // 格式化类型
	Formatterstring string `json:"formatterstring"  xorm:"'FORMATTERSTRING'"`  // 格式化字符
	Remark          string `json:"remark"  xorm:"'REMARK'"`                    // 备注
	Needsummary     uint32 `json:"needsummary"  xorm:"'NEEDSUMMARY'"`          // 是否需要汇总 - 0:不需要 1:需要
	Summarytype     uint32 `json:"summarytype"  xorm:"'SUMMARYTYPE'"`          // 汇总类型 - 1:加总 2:最后一个
	Groupname       string `json:"groupname"  xorm:"'GROUPNAME'"`              // 表头分组名称
}

// TableName is TABLECOLUMNCONFIG
func (Tablecolumnconfig) TableName() string {
	return "TABLECOLUMNCONFIG"
}

// Enumdicitem 枚举项字典表
type Enumdicitem struct {
	Autoid          int64  `json:"autoid"  xorm:"'AUTOID'" binding:"required"`             // 自增ID
	Enumdicid       int32  `json:"enumdicid"  xorm:"'ENUMDICID'" binding:"required"`       // 所属枚举ID
	Enumdiccode     string `json:"enumdiccode"  xorm:"'ENUMDICCODE'" binding:"required"`   // 所属枚举代码
	Enumdicname     string `json:"enumdicname"  xorm:"'ENUMDICNAME'"`                      // 枚举项名称
	Enumitemname    int64  `json:"enumitemname"  xorm:"'ENUMITEMNAME'" binding:"required"` // 枚举项值
	Enumitemstatus  int32  `json:"enumitemstatus"  xorm:"'ENUMITEMSTATUS'"`                // 枚举项状态 - 1.启用 2.不启用
	Bankmappedvalue string `json:"bankmappedvalue"  xorm:"'BANKMAPPEDVALUE'"`              // 银行服务对应值
	Remark          string `json:"remark"  xorm:"'REMARK'"`                                // 备注
	Enumitemvalue   string `json:"enumitemvalue"  xorm:"'ENUMITEMVALUE'"`                  // 通用值 - [币种通用简写]
	Param1          string `json:"param1"  xorm:"'PARAM1'"`                                // 参数1[币种:币种小数位]
	Param2          string `json:"param2"  xorm:"'PARAM2'"`                                // 参数1[币种:币种显示单位]
}

// TableName is ENUMDICITEM
func (Enumdicitem) TableName() string {
	return "ENUMDICITEM"
}

// Noticemsg 公告消息表
type Noticemsg struct {
	Autoid        int32     `json:"autoid"  xorm:"'AUTOID'" binding:"required"` // 自增ID
	Msgtype       int32     `json:"msgtype"  xorm:"'MSGTYPE'"`                  // 消息类型 - 1:公告通知 2:系统消息 3:商品到期提货通知
	Title         string    `json:"title"  xorm:"'TITLE'"`                      // 标题
	Content       string    `json:"content"  xorm:"'CONTENT'"`                  // 内容
	Scheduletime  time.Time `json:"scheduletime"  xorm:"'SCHEDULETIME'"`        // 计划发送时间
	Publisher     string    `json:"publisher"  xorm:"'PUBLISHER'"`              // 消息发布者
	Sendtype      int32     `json:"sendtype"  xorm:"'SENDTYPE'"`                // 推送方式 - 1：全体广播 2：按会员广播 3：个人推送 4:按会员广播(仅会员)
	Userid        int64     `json:"userid"  xorm:"'USERID'"`                    // 会员/投资者ID推送方式 为 个人时，填写投资者ID
	Istop         int32     `json:"istop"  xorm:"'ISTOP'"`                      // 是否置顶 - 0:不置顶 1:置顶
	Sentstatus    int32     `json:"sentstatus"  xorm:"'SENTSTATUS'"`            // 推送状态 - 0:未推送 1:已推送 2:审核拒绝
	Endtime       time.Time `json:"endtime"  xorm:"'ENDTIME'"`                  // 结束时间
	Creatorid     int64     `json:"creatorid"  xorm:"'CREATORID'"`              // 建仓人
	Createtime    time.Time `json:"createtime"  xorm:"'CREATETIME'"`            // 创建时间
	Auditoruserid int64     `json:"auditoruserid"  xorm:"'AUDITORUSERID'"`      // 审核人
	Audittime     time.Time `json:"audittime"  xorm:"'AUDITTIME'"`              // 审核日期
	Auditremark   string    `json:"auditremark"  xorm:"'AUDITREMARK'"`          // 审核备注
	Msgiconurl    string    `json:"msgiconurl"  xorm:"'MSGICONURL'"`            // 消息图标Url
}

// TableName is NOTICEMSG
func (Noticemsg) TableName() string {
	return "NOTICEMSG"
}

// Memberrecv 会员消息接受表
type Memberrecv struct {
	Msgid    int32 `json:"msgid"  xorm:"'MSGID'" binding:"required"`       // 消息ID
	Memberid int64 `json:"memberid"  xorm:"'MEMBERID'" binding:"required"` // 会员用户ID
}

// TableName is MEMBERRECV
func (Memberrecv) TableName() string {
	return "MEMBERRECV"
}

// Msgreceiver 管理端消息接收者
type Msgreceiver struct {
	Autoid       int32     `json:"autoid"  xorm:"'AUTOID'" binding:"required"`             // 消息ID
	Managerid    int64     `json:"managerid"  xorm:"'MANAGERID'" binding:"required"`       // 用户ID
	Readstatus   int32     `json:"readstatus"  xorm:"'READSTATUS'" binding:"required"`     // 消息状态 - 1：未阅读 2：已阅读 3：已删除
	Receivertype int32     `json:"receivertype"  xorm:"'RECEIVERTYPE'" binding:"required"` // 接收用户类型 - 1:管理端用户 2:终端用户
	Updatetime   time.Time `json:"updatetime"  xorm:"'UPDATETIME'"`                        // 更新时间
}

// TableName is MSGRECEIVER
func (Msgreceiver) TableName() string {
	return "MSGRECEIVER"
}

// Division 行政区域表
type Division struct {
	Autoid        int64     `json:"autoid"  xorm:"'AUTOID'" binding:"required"`             // 自增ID
	Divisioncode  string    `json:"divisioncode"  xorm:"'DIVISIONCODE'" binding:"required"` // 行政代码
	Shortcode     string    `json:"shortcode"  xorm:"'SHORTCODE'"`                          // 地区简码
	Parentcode    string    `json:"parentcode"  xorm:"'PARENTCODE'"`                        // 上级行政代码
	Divisionlevel string    `json:"divisionlevel"  xorm:"'DIVISIONLEVEL'"`                  // 行政级别
	Divisionname  string    `json:"divisionname"  xorm:"'DIVISIONNAME'"`                    // 行政名称
	Pathname      string    `json:"pathname"  xorm:"'PATHNAME'"`                            // 路径名称
	Separablename string    `json:"separablename"  xorm:"'SEPARABLENAME'"`                  // 可拆分的全称
	Postcode      string    `json:"postcode"  xorm:"'POSTCODE'"`                            // 邮政编码
	Modifytime    time.Time `json:"modifytime"  xorm:"'MODIFYTIME'"`                        // 修改时间
	Modifierid    int64     `json:"modifierid"  xorm:"'MODIFIERID'"`                        // 修改人
}

// TableName is DIVISION
func (Division) TableName() string {
	return "DIVISION"
}

// Szdz2imageconfig 终端图片配置  - 通用功能
type Szdz2imageconfig struct {
	Configid   int64     `json:"configid"  xorm:"'CONFIGID'" binding:"required"` // 配置ID(SEQ_SZDZ2_IMAGECONFIG)
	Imagetype  int32     `json:"imagetype"  xorm:"'IMAGETYPE'"`                  // 类型 - 1:首页轮播(移动) 2:我的(移动)
	Title      string    `json:"title"  xorm:"'TITLE'"`                          // 标题
	Imagepath  string    `json:"imagepath"  xorm:"'IMAGEPATH'"`                  // 图片
	URL        string    `json:"url"  xorm:"'URL'"`                              // 链接(地址或商品ID)
	Sort       int64     `json:"sort"  xorm:"'SORT'"`                            // 排序
	Isshow     int32     `json:"isshow"  xorm:"'ISSHOW'"`                        // 是否展示 - 0:不展示 1:展示
	Createdate time.Time `json:"createdate"  xorm:"'CREATEDATE'"`                // 创建时间
	Creatorid  int64     `json:"creatorid"  xorm:"'CREATORID'"`                  // 创建人
	Updatedate time.Time `json:"updatedate"  xorm:"'UPDATEDATE'"`                // 修改时间
	Modifierid int64     `json:"modifierid"  xorm:"'MODIFIERID'"`                // 操作人
	Areauserid int64     `json:"areauserid"  xorm:"'AREAUSERID'"`                // 所属机构ID
	Urltype    int32     `json:"urltype"  xorm:"'URLTYPE'"`                      // 链接类型 - 1:直接地址 2:商品ID
}

// TableName is SZDZ2_IMAGECONFIG
func (Szdz2imageconfig) TableName() string {
	return "SZDZ2_IMAGECONFIG"
}

// Errorcode 异常代码表
type Errorcode struct {
	Errorid     int64  `json:"errorid"  xorm:"'ERRORID'" binding:"required"` // 异常ID
	Errorcode   string `json:"errorcode"  xorm:"'ERRORCODE'"`                // 异常代码
	Modulecode  string `json:"modulecode"  xorm:"'MODULECODE'"`              // 所属模块
	Operatecode string `json:"operatecode"  xorm:"'OPERATECODE'"`            // 所属操作
	Description string `json:"description"  xorm:"'DESCRIPTION'"`            // 异常描述
}

// TableName is ERRORCODE
func (Errorcode) TableName() string {
	return "ERRORCODE"
}

// Systemparam 系统参数表
type Systemparam struct {
	Paramcode   string    `json:"paramcode"  xorm:"'PARAMCODE'" binding:"required"` // 参数代码
	Paramname   string    `json:"paramname"  xorm:"'PARAMNAME'"`                    // 参数名
	Paramvalue  string    `json:"paramvalue"  xorm:"'PARAMVALUE'"`                  // 参数值
	Paramtype   int32     `json:"paramtype"  xorm:"'PARAMTYPE'"`                    // 参数类型  0-通用 1-管理端 2-终端  3-后台 4-商城
	Showflag    int32     `json:"showflag"  xorm:"'SHOWFLAG'"`                      // 是否显示0 不显示   1 显示
	Modifyflag  int32     `json:"modifyflag"  xorm:"'MODIFYFLAG'"`                  // 修改标志0 不允许修改   1 允许修改
	Serverip    string    `json:"serverip"  xorm:"'SERVERIP'"`                      // 限制服务器IP,备份参数，现在界面不用显示0.0.0.0则无限制，是通用参数192.168.0.211 指定服务器的参数配置
	Userid      int64     `json:"userid"  xorm:"'USERID'"`                          // 更新用户ID
	Loginid     string    `json:"loginid"  xorm:"'LOGINID'"`                        // 更新用户账号,登录账号
	Updatetime  time.Time `json:"updatetime"  xorm:"'UPDATETIME'"`                  // 更新时间
	Remark      string    `json:"remark"  xorm:"'REMARK'"`                          // 备注
	Paramvalue2 string    `json:"paramvalue2"  xorm:"'PARAMVALUE2'"`                // 参数值(CLOB)
	Regexpress  string    `json:"regexpress"  xorm:"'REGEXPRESS'"`                  // 正则表达式
}

// TableName is SYSTEMPARAM
func (Systemparam) TableName() string {
	return "SYSTEMPARAM"
}

// Ermcpmemberfuncmenu 企业成员菜单表
type Ermcpmemberfuncmenu struct {
	Userid       int64     `json:"userid"  xorm:"'USERID'" binding:"required"`             // 用户ID
	Roleid       int32     `json:"roleid"  xorm:"'ROLEID'" binding:"required"`             // 角色权限ID(0-管理员 22-业务员 23-跟单员 24-交易员)
	Resourcecode string    `json:"resourcecode"  xorm:"'RESOURCECODE'" binding:"required"` // 菜单代码
	Modifysrc    int32     `json:"modifysrc"  xorm:"'MODIFYSRC'"`                          // 修改来源 - 1:管理端 2:终端
	Modifytime   time.Time `json:"modifytime"  xorm:"'MODIFYTIME'"`                        // 修改时间
	Modifyid     int64     `json:"modifyid"  xorm:"'MODIFYID'"`                            // 修改人
	Modifyremark string    `json:"modifyremark"  xorm:"'MODIFYREMARK'"`                    // 修改备注
	Isvalid      int32     `json:"isvalid"  xorm:"'ISVALID'"`                              // 是否有效 - 0:无效 1:有效
}

// TableName is ERMCP_MEMBERFUNCMENU
func (Ermcpmemberfuncmenu) TableName() string {
	return "ERMCP_MEMBERFUNCMENU"
}

// QuotePrimaryMenu 报价牌一级分类菜单
type QuotePrimaryMenu struct {
	Index        int                  `json:"Index"`        // 序号
	Key          string               `json:"Key"`          // 键名
	Name         string               `json:"Name"`         // 菜单名称
	SubTitleType int                  `json:"SubTitleType"` // 子菜单标题模式：0-市场名称；1-外部交易所名称
	TradeModes   string               `json:"TradeModes"`   // 包含市场交易类型
	SubMenus     []QuoteSecondaryMenu `json:"SubMenus"`     // 子菜单
}

// QuoteSecondaryMenu 报价牌二级分类菜单
type QuoteSecondaryMenu struct {
	Index          int    `json:"Index"`                                  // 序号
	MarketID       int    `json:"MarketID"`                               // 市场ID
	TradeMode      int    `json:"TradeMode"`                              // 交易模式
	MenuTitle      string `json:"MenuTitle" xorm:"'ExExchangeName'"`      // 菜单标题（市场名称或外部交易所名称）
	GoodsGroupIDs  []int  `json:"GoodsGroupIDs"`                          // 商品组ID列表
	ExExchangeID   int    `json:"ExExchangeID" xorm:"'ExExchangeID'"`     // 外部交易所ID
	ExExchangeCode string `json:"ExExchangeCode" xorm:"'ExExchangeCode'"` // 外部交易所代码
}

// OperationPrimaryMenu 一级功能菜单
type OperationPrimaryMenu struct {
	Key      string                   `json:"Key"`      // 菜单KEY
	Label    string                   `json:"Label"`    // 菜单标题
	Children []OperationSecondaryMenu `json:"Children"` // 二级功能菜单
}

// OperationSecondaryMenu 二级功能菜单
type OperationSecondaryMenu struct {
	Key     string             `json:"Key"`     // 菜单KEY
	Label   string             `json:"Label"`   // 菜单标题
	TabList []OperationTabMenu `json:"TabList"` // 三级功能菜单
}

// OperationTabMenu 三级功能菜单
type OperationTabMenu struct {
	Key   string `json:"Key"`   // 菜单KEY
	Label string `json:"Label"` // 菜单标题
}

// GetQuoteMenu 获取行情报价牌分类菜单
func GetQuoteMenu(loginID int) ([]QuotePrimaryMenu, error) {
	engine := db.GetEngine()
	rst := make([]QuotePrimaryMenu, 0)

	// 账户下有权限的市场ID列表
	var marketIDs []int

	// 获取账户类型 - 1:交易所 2:机构 3:会员子机构 4:经纪人 5:投资者 6:客户  (目前可能登录交易端的账号类型为 2 5)
	userAccount := new(Useraccount)
	has, err := engine.Join("INNER", "LOGINACCOUNT", "USERACCOUNT.UserID = LOGINACCOUNT.UserID").Where("LOGINACCOUNT.LoginID = ?", loginID).Get(userAccount)
	if err != nil || !has {
		return nil, err
	}
	if userAccount.Usertype == 5 {
		// 如果账户类型为5（投资者），则需要通过其所属经济会员来获取市场权限（表：AreaRoleMarket, 条件：对应市场状态为正常；角色类型：经济会员）
		if err := engine.Table("AREAROLEMARKET").
			Cols("AREAROLEMARKET.MARKETID").
			Join("INNER", "MARKET", "MARKET.MARKETID = AREAROLEMARKET.MARKETID").
			Where("MARKET.MARKETSTATUS = 2 and AREAROLEMARKET.ROLETYPE = 7 and AREAROLEMARKET.AREAUSERID = ?", userAccount.Memberuserid).Find(&marketIDs); err != nil {
			return nil, err
		}
	} else {
		// 非投资者账号直接通过资金账号获取市场权限（表：TAAccountMarket）
		var taAccounts []string // 账户下所有资金账户

		// 先要获取当前登录账户对应的资金账户；如果为外部子资金账号（TaAccount.TaAccountType = 1），则使用TaAccount.FromAccountID来获取市场权限
		type taAccount struct {
			AccountID     int `xorm:"ACCOUNTID"`
			TaAccountType int `xorm:"TAACCOUNTTYPE"`
			FromAccountID int `xorm:"FROMACCOUNTID"`
		}
		datas := make([]taAccount, 0)
		if err := engine.Table("LOGINTAACCOUNT").
			Join("INNER", "TAACCOUNT", "LOGINTAACCOUNT.ACCOUNTID = TAACCOUNT.ACCOUNTID").
			Cols("TAACCOUNT.ACCOUNTID", "TAACCOUNT.TAACCOUNTTYPE", "TAACCOUNT.FROMACCOUNTID").
			Where("LOGINTAACCOUNT.LOGINID = ?", loginID).Find(&datas); err != nil {
			return nil, err
		}
		// 如果一条记录都没有(未配置自营会员资金账户)则直接通过资金账户表获取
		if len(datas) == 0 {
			// 这里要注意，TaAccount表与LoginAccount表关联时，要使用TaAccount.RelatedUserID
			if err := engine.Table("TAACCOUNT").
				Join("INNER", "LOGINACCOUNT", "TAACCOUNT.RELATEDUSERID = LOGINACCOUNT.USERID").
				Cols("TAACCOUNT.ACCOUNTID", "TAACCOUNT.TAACCOUNTTYPE", "TAACCOUNT.FROMACCOUNTID").
				Where("LOGINACCOUNT.LOGINID = ?", loginID).Find(&datas); err != nil {
				return nil, err
			}
		}
		for _, v := range datas {
			if v.TaAccountType == 1 {
				// 外部资金账户使用TaAccount.FromAccountID来获取市场权限
				taAccounts = append(taAccounts, strconv.Itoa(v.FromAccountID))
			} else {
				taAccounts = append(taAccounts, strconv.Itoa(v.AccountID))
			}
		}

		// 获取资金账户对应的市场权限（有权限的市场ID）
		if len(taAccounts) > 0 {
			// taAccountStr := strings.Join(taAccounts, ",")
			if err := engine.Table("TAACCOUNTMARKET").
				Join("INNER", "MARKET", "MARKET.MARKETID = TAACCOUNTMARKET.MARKETID").
				Cols("TAACCOUNTMARKET.MARKETID").
				In("TAACCOUNTMARKET.ACCOUNTID", taAccounts).
				And("MARKET.MARKETSTATUS = 2").Find(&marketIDs); err != nil {
				return nil, err
			}
		}
	}

	// ********************* 构建行情报价牌菜单 *********************
	datas := make([]Funcmenulist, 0)
	if err := engine.Join("INNER", "ROLEFUNCMENU", "FUNCMENULIST.RESOURCECODE = ROLEFUNCMENU.RESOURCECODE").
		Where("FUNCMENULIST.PARENTCODE = 'trader_master_menu'").
		Asc("SORT").
		Find(&datas); err != nil {
		return nil, err
	}
	// 构建一级菜单对象
	for i, v := range datas {
		quotePrimaryMenu := QuotePrimaryMenu{
			Index:    i,
			Key:      v.Resourcecode,
			Name:     v.Resourcename,
			SubMenus: make([]QuoteSecondaryMenu, 0),
		}
		// 跳过自选
		if v.Resourcecode == "optional" {
			rst = append(rst, quotePrimaryMenu)
			continue
		}
		// URL：模式，0-使用市场名称，1-使用外部交易所名称；Remark：包含市场交易模式
		quotePrimaryMenu.SubTitleType, _ = strconv.Atoi(v.URL)
		quotePrimaryMenu.TradeModes = v.Remark

		// 如果传入的LoginID获取不到有权限的市场ID列表（有可能LoginID是错误的），则不构建二级子菜单
		if len(marketIDs) == 0 {
			rst = append(rst, quotePrimaryMenu)
			continue
		}

		// 构建二级子菜单对象
		marketIDsStr := utils.JoinItoString(marketIDs, ",")
		if quotePrimaryMenu.SubTitleType == 0 {
			// 获取目标交易模式的市场信息
			markets := make([]Market, 0)
			if err := engine.Where(fmt.Sprintf(`TradeMode in (%s) and MarketID in (%s)`, quotePrimaryMenu.TradeModes, marketIDsStr)).Find(&markets); err != nil {
				return nil, err
			}
			// 使用市场名称
			for mi, mv := range markets {
				quoteSecondaryMenu := QuoteSecondaryMenu{
					Index:         mi,
					MarketID:      int(mv.Marketid),
					TradeMode:     int(mv.Trademode),
					MenuTitle:     mv.Marketname,
					GoodsGroupIDs: make([]int, 0),
				}
				quotePrimaryMenu.SubMenus = append(quotePrimaryMenu.SubMenus, quoteSecondaryMenu)
			}
		} else {
			// 使用外部交易所名称
			quoteSecondaryMenus := make([]QuoteSecondaryMenu, 0)
			sql := fmt.Sprintf(`select distinct
									e.autoid ExExchangeID,
									e.ExExchangeName,
									e.ExExchangeCode 
								from ExternalExchange e
								inner join goodsgroup g  on g.exexchangeid = e.autoid
								inner join Market m on g.marketid = m.marketid
								where m.trademode in (%s) and m.marketid in (%s)`, quotePrimaryMenu.TradeModes, marketIDsStr)
			if err := engine.SQL(sql).Find(&quoteSecondaryMenus); err != nil {
				return nil, err
			}
			// 获取外部交易所对应的商品组信息
			for ei, ev := range quoteSecondaryMenus {
				q := &quoteSecondaryMenus[ei]
				q.Index = ei
				// 商品组列表
				goodsgroups := make([]Goodsgroup, 0)
				if err := engine.Where("Exexchangeid = ?", ev.ExExchangeID).Find(&goodsgroups); err != nil {
					return nil, err
				}
				marketID := 0
				var goodsGroupIDs []int
				for _, gv := range goodsgroups {
					marketID = int(gv.Marketid)
					goodsGroupIDs = append(goodsGroupIDs, int(gv.Goodsgroupid))
				}
				q.MarketID = marketID
				q.GoodsGroupIDs = goodsGroupIDs
			}
			quotePrimaryMenu.SubMenus = quoteSecondaryMenus
		}

		rst = append(rst, quotePrimaryMenu)
	}

	return rst, nil
}

// GetOperationMenu 获取功能菜单
func GetOperationMenu() ([]OperationPrimaryMenu, error) {
	engine := db.GetEngine()
	rst := make([]OperationPrimaryMenu, 0)

	// 获取一级功能菜单
	opm := make([]Funcmenulist, 0)
	if err := engine.Join("INNER", "ROLEFUNCMENU", "FUNCMENULIST.RESOURCECODE = ROLEFUNCMENU.RESOURCECODE").
		Where("FUNCMENULIST.PARENTCODE = 'trader_operation_master_menu'").
		Asc("SORT").
		Find(&opm); err != nil {
		return nil, err
	}
	for _, pv := range opm {
		var operationPrimaryMenu = OperationPrimaryMenu{
			Key:      pv.Resourcecode,
			Label:    pv.Resourcename,
			Children: make([]OperationSecondaryMenu, 0),
		}

		// 获取二级功能菜单
		osm := make([]Funcmenulist, 0)
		if err := engine.Join("INNER", "ROLEFUNCMENU", "FUNCMENULIST.RESOURCECODE = ROLEFUNCMENU.RESOURCECODE").
			Where("FUNCMENULIST.PARENTCODE = ?", operationPrimaryMenu.Key).
			Asc("SORT").
			Find(&osm); err != nil {
			return nil, err
		}
		for _, sv := range osm {
			var operationSecondaryMenu = OperationSecondaryMenu{
				Key:     sv.Resourcecode,
				Label:   sv.Resourcename,
				TabList: make([]OperationTabMenu, 0),
			}

			// 获取三级功能菜单
			otm := make([]Funcmenulist, 0)
			if err := engine.Join("INNER", "ROLEFUNCMENU", "FUNCMENULIST.RESOURCECODE = ROLEFUNCMENU.RESOURCECODE").
				Where("FUNCMENULIST.PARENTCODE = ?", operationSecondaryMenu.Key).
				Asc("SORT").
				Find(&otm); err != nil {
				return nil, err
			}
			for _, tv := range otm {
				var operationTabMenu = OperationTabMenu{
					Key:   tv.Resourcecode,
					Label: tv.Resourcename,
				}

				operationSecondaryMenu.TabList = append(operationSecondaryMenu.TabList, operationTabMenu)
			}

			operationPrimaryMenu.Children = append(operationPrimaryMenu.Children, operationSecondaryMenu)
		}

		rst = append(rst, operationPrimaryMenu)
	}

	return rst, nil
}

// GetClientTableDefines 获取终端列表定义信息
func GetClientTableDefines(tableKey string, tableType int) ([]Tabledefine, error) {
	engine := db.GetEngine()

	tableDefiles := make([]Tabledefine, 0)
	s := engine.Table("TABLEDEFINE")
	if tableType != 0 {
		s = s.Where("TABLEDEFINE.TableType = ?", tableType)
	}
	if len(tableKey) > 0 {
		s = s.And("TABLEDEFINE.TableKey = ?", tableKey)
	}
	if err := s.Find(&tableDefiles); err != nil {
		return nil, err
	}

	return tableDefiles, nil
}

// GetClientTableColumns 获取客户端指定表的列头信息
// tableKey 列表Key
func GetClientTableColumns(tableKey string, tableType int) ([]Tablecolumnconfig, error) {
	engine := db.GetEngine()

	// 获取列表数据 2 - H5; 3 - APP
	tablecolumnconfigs := make([]Tablecolumnconfig, 0)
	// 这里的表名必须要大写 （Oracle的表名都是大写）
	if err := engine.Join("INNER", "TABLEDEFINE", "TABLEDEFINE.TableKey = TABLECOLUMNCONFIG.TableKey").
		Where("TABLEDEFINE.TableType = ?", tableType).And("TABLEDEFINE.TableKey = ?", tableKey).Find(&tablecolumnconfigs); err != nil {
		return nil, err
	}

	return tablecolumnconfigs, nil
}

// GetNotices 获取指定账户的通知信息（终端）
// 参数 loginID int 登录账号
// 参数 msgType int 消息类型 - 1:公告通知 2:系统消息
// 参数 onlyUnRead bool 是否只获取未读信息
func GetNotices(loginID, msgType int, onlyUnRead bool) ([]Noticemsg, error) {
	engine := db.GetEngine()

	rst := make([]Noticemsg, 0)

	// 获取登录账号所属会员信息
	var userAccount Useraccount
	if has, _ := engine.Join("LEFT", "LOGINACCOUNT", "LOGINACCOUNT.USERID = USERACCOUNT.USERID").
		Where("LOGINACCOUNT.LOGINID = ?", loginID).Get(&userAccount); !has {
		return nil, errors.New("获取登录账号所属会员ID失败")
	}

	// 获取登录账号已读公告ID, 这里要使用的是个人的UserID来查询
	var msgReceivers []int
	engine.Table("MSGRECEIVER").Select("AUTOID").Where("RECEIVERTYPE = 2 and MANAGERID = ?", userAccount.Userid).Find(&msgReceivers)

	// 查询SENDTYPE = 1的数据
	datas1 := make([]Noticemsg, 0)
	s := engine.Where("SENDTYPE = 1 and SYSDATE > SCHEDULETIME and SYSDATE < ENDTIME and SENTSTATUS=1").
		And("PUBLISHER = ? or PUBLISHER in (select USERACCOUNT.USERID from USERACCOUNT where USERACCOUNT.USERTYPE = 1)", userAccount.Memberuserid)
	if msgType > 0 {
		s = s.And("MSGTYPE = ?", msgType)
	}
	if onlyUnRead && len(msgReceivers) > 0 {
		s = s.NotIn("AUTOID", msgReceivers)
	}
	if err := s.Find(&datas1); err != nil {
		// 查询失败
		return nil, err
	}
	rst = append(rst, datas1...)

	// 查询SENDTYPE = 2的数据
	datas2 := make([]Noticemsg, 0)
	s = engine.Where(fmt.Sprintf(`AUTOID in (select MEMBERRECV.MSGID from MEMBERRECV where MEMBERRECV.MEMBERID = %d)`, userAccount.Memberuserid)).
		And("SENDTYPE = 2 and SYSDATE > SCHEDULETIME and SYSDATE < ENDTIME and SENTSTATUS=1")
	if msgType > 0 {
		s = s.And("MSGTYPE = ?", msgType)
	}
	if onlyUnRead && len(msgReceivers) > 0 {
		s = s.NotIn("AUTOID", msgReceivers)
	}
	if err := s.Find(&datas2); err != nil {
		// 查询失败
		return nil, err
	}
	rst = append(rst, datas2...)

	// 查询SENDTYPE = 3的数据
	datas3 := make([]Noticemsg, 0)
	s = engine.Where(fmt.Sprintf(`USERID = (SELECT USERID FROM LOGINACCOUNT WHERE LOGINID = %d)`, loginID)).
		And("SENDTYPE = 3 and SYSDATE > SCHEDULETIME and SYSDATE < ENDTIME and SENTSTATUS=1")
	if msgType > 0 {
		s = s.And("MSGTYPE = ?", msgType)
	}
	if onlyUnRead && len(msgReceivers) > 0 {
		s = s.NotIn("AUTOID", msgReceivers)
	}
	if err := s.Find(&datas3); err != nil {
		// 查询失败
		return nil, err
	}
	rst = append(rst, datas3...)

	return rst, nil
}

// GetEnumDicItem 获取枚举信息数组
// 参数 enumDicCode string 所属枚举代码，必填
// 参数 enumItemName int 枚举项值，选填，不需要则传入0
// 返回 []Enumdicitem 枚举信息数组
// 返回 error error
func GetEnumDicItem(enumDicCode string, enumItemName int) ([]Enumdicitem, error) {
	engine := db.GetEngine()

	enumDicItems := make([]Enumdicitem, 0)
	session := engine.Where("ENUMDICCODE = ?", enumDicCode)
	if enumItemName > 0 {
		session = session.And("ENUMITEMNAME = ?", enumItemName)
	}
	if err := session.Find(&enumDicItems); err != nil {
		return nil, err
	}

	return enumDicItems, nil
}

// GetEnums 获取所有枚举信息
func GetEnums(autoID int) ([]Enumdicitem, error) {
	engine := db.GetEngine()

	enumDicItems := make([]Enumdicitem, 0)
	session := engine.Table("ENUMDICITEM")
	if autoID != 0 {
		session = session.Where("AUTOID > ?", autoID)
	}
	if err := session.Find(&enumDicItems); err != nil {
		return nil, err
	}

	return enumDicItems, nil
}

// GetProvincesAndCities 获取省市信息数组
// 参数 provinceID int 省ID，选填
// 返回 []Division 枚举信息数组
// 返回 error error
func GetProvincesAndCities(provinceID int) ([]Division, error) {
	engine := db.GetEngine()

	divisions := make([]Division, 0)
	session := engine.Where("DIVISIONLEVEL = 'province' or DIVISIONLEVEL = 'city'")
	if provinceID > 0 {
		session = session.And("AUTOID = ?", provinceID)
	}
	if err := session.Find(&divisions); err != nil {
		return nil, err
	}

	return divisions, nil
}

// GetDivisions 获取所有区域信息
func GetDivisions() ([]Division, error) {
	engine := db.GetEngine()

	divisions := make([]Division, 0)
	if err := engine.Find(&divisions); err != nil {
		return nil, err
	}

	return divisions, nil
}

// GetImageConfigs 获取轮播图配置信息
func GetImageConfigs(imageType int) ([]Szdz2imageconfig, error) {
	engine := db.GetEngine()

	imageConfigs := make([]Szdz2imageconfig, 0)
	session := engine.Where("ISSHOW = 1")
	if imageType > 0 {
		session = session.And("IMAGETYPE = ?", imageType)
	}
	session = session.Asc("SORT")
	if err := session.Find(&imageConfigs); err != nil {
		return nil, err
	}

	return imageConfigs, nil
}

// GetServerTime 获取服务器时间
func GetServerTime() (*string, error) {
	engine := db.GetEngine()

	rst, err := engine.QueryString("select sysdate from dual")
	if err != nil {
		return nil, err
	}
	if len(rst) > 0 {
		r := rst[0]["SYSDATE"]
		return &r, nil
	}

	return nil, nil
}

// QueryErrorInfosRsp 获取数据库错误信息模型
type QueryErrorInfosRsp struct {
	Errorid     int64  `json:"errorid"  xorm:"'ERRORID'" binding:"required"` // 异常ID
	Errorcode   string `json:"errorcode"  xorm:"'ERRORCODE'"`                // 异常代码
	Modulecode  string `json:"modulecode"  xorm:"'MODULECODE'"`              // 所属模块
	Operatecode string `json:"operatecode"  xorm:"'OPERATECODE'"`            // 所属操作
	Description string `json:"description"  xorm:"'DESCRIPTION'"`            // 异常描述
	Rownumber   string `json:"rownumber" xorm:"ROWNUMBER"`                   // 行号
}

// GetErrorInfos 获取数据库错误信息
func GetErrorInfos(rowNumber string) ([]QueryErrorInfosRsp, error) {
	engine := db.GetEngine()

	errorCodes := make([]QueryErrorInfosRsp, 0)
	session := engine.Table("ERRORCODE E").Select("E.*, rowid ROWNUMBER").Where("1 = 1")
	if len(rowNumber) != 0 {
		session = session.And("rowid > ?", rowNumber)
	}
	if err := session.Find(&errorCodes); err != nil {
		return nil, err
	}

	return errorCodes, nil
}

// GetTableUpdateTimeStamp 获取目标表最后记录变更时间
func GetTableUpdateTimeStamp(tableName string) (*string, error) {
	engine := db.GetEngine()

	var timeStamp string
	if _, err := engine.SQL(fmt.Sprintf("SELECT to_char(SCN_TO_TIMESTAMP(MAX(ora_rowscn)), 'yyyy-MM-dd hh24:mi:ss') TIMESTAMP from %s", tableName)).
		Get(&timeStamp); err != nil {
		return nil, err
	}

	return &timeStamp, nil
}

// GetSystemParams 获取系统参数信息
func GetSystemParams() ([]Systemparam, error) {
	systemParams := make([]Systemparam, 0)
	if err := db.GetEngine().Where("PARAMTYPE in (0,2,3)").Find(&systemParams); err != nil {
		return nil, err
	}

	return systemParams, nil
}

// GetRoleFuncMenuLists 获取终端权限（交易所版本）
func GetRoleFuncMenuLists(loginID int, resourceCode string) ([]Funcmenulist, error) {
	funcMenuLists := make([]Funcmenulist, 0)

	sql := fmt.Sprintf(`select F.* from FUNCMENULIST F where F.MENUTYPE = 3 
						and 
						F.RESOURCECODE in 
						(select R.RESOURCECODE from ROLEFUNCMENU R 
							inner join LOGINACCOUNT L on L.CLIENTROLEID = R.ROLEID where L.LOGINID = %d)`, loginID)
	if len(resourceCode) != 0 {
		sql += fmt.Sprintf(" and F.RESOURCECODE = '%s'", resourceCode)
	}
	if err := db.GetEngine().SQL(sql).Find(&funcMenuLists); err != nil {
		return nil, err
	}

	return funcMenuLists, nil
}

// GetErmcpRoleFuncMenuLists 获取终端权限（企业风管专用，区分企业和企业成员）
func GetErmcpRoleFuncMenuLists(loginID int, resourceCode string) ([]Funcmenulist, error) {
	funcMenuLists := make([]Funcmenulist, 0)

	// 获取对应的Loginaccount信息
	var loginAccount Loginaccount
	has, err := db.GetEngine().Where("LOGINID = ?", loginID).Get(&loginAccount)
	if err != nil {
		return nil, err
	}
	if !has {
		return nil, errors.New("没有对应的登录用户信息")
	}

	// 获取对应的Useraccount信息
	var userAccount Useraccount
	has, err = db.GetEngine().Table("USERACCOUNT U").
		Join("INNER", "LOGINACCOUNT L", "L.USERID = U.USERID").
		Where("L.LOGINID = ?", loginID).Get(&userAccount)
	if err != nil {
		return nil, err
	}
	if !has {
		return nil, errors.New("没有对应的用户信息")
	}

	// 是否企业
	isEnterprise := false
	// 查询机构角色表
	var arearole Arearole
	arearoles, err := arearole.GetAreaRoles(int(userAccount.Userid))
	if err != nil {
		return nil, err
	}
	if len(arearoles) > 0 {
		arearole = arearoles[0]
		if arearole.Suborgtype == 0 {
			isEnterprise = true
		}
	}

	// 判断是企业还是企业成员
	if userAccount.Usertype == 2 && isEnterprise {
		// UserType=2：企业。原逻辑：根据LoginAccount表"ClientRoleID"查询”ERMCP_MemberFuncMenu“表中该用户对应角色的菜单权限
		sql := fmt.Sprintf(`select F.* from FUNCMENULIST F where F.MENUTYPE = 3 
							and 
							F.RESOURCECODE in 
							(select R.RESOURCECODE from ERMCP_MEMBERFUNCMENU R 
								where R.ISVALID = 1 and R.USERID = %d and R.ROLEID = %d)`, loginAccount.Userid, loginAccount.Clientroleid)
		if len(resourceCode) != 0 {
			sql += fmt.Sprintf(" and F.RESOURCECODE = '%s'", resourceCode)
		}
		if err := db.GetEngine().SQL(sql).Find(&funcMenuLists); err != nil {
			return nil, err
		}
	} else if userAccount.Usertype == 6 || userAccount.Usertype == 7 || (userAccount.Usertype == 2 && !isEnterprise) {
		// UserType=7：企业成员
		// 查询本用户的角色ID  - AreaRole表
		// 根据 ”ParentuserID ”和 自己的角色查询“ERMCP_MemberFuncMenu“表中的菜单权限
		areaRoles := make([]Arearole, 0)
		if err := db.GetEngine().Where("USERID = ?", loginAccount.Userid).Find(&areaRoles); err != nil {
			return nil, err
		}
		if len(areaRoles) == 0 {
			// 无角色，返回空数组s
			return make([]Funcmenulist, 0), nil
		}
		// 构建RoleID列表
		roleIDs := ""
		for _, areaRole := range areaRoles {
			roleIDs += "," + strconv.Itoa(int(areaRole.Roletype))
		}
		if len(roleIDs) > 0 {
			roleIDs = roleIDs[1:]
		}
		sql := fmt.Sprintf(`select F.* from FUNCMENULIST F where F.MENUTYPE = 3 
							and 
							F.RESOURCECODE in 
							(select R.RESOURCECODE from ERMCP_MEMBERFUNCMENU R 
								where R.ISVALID = 1 and R.USERID = %d and R.ROLEID in (%s))`, userAccount.Parentuserid, roleIDs)
		if len(resourceCode) != 0 {
			sql += fmt.Sprintf(" and F.RESOURCECODE = '%s'", resourceCode)
		}
		if err := db.GetEngine().SQL(sql).Find(&funcMenuLists); err != nil {
			return nil, err
		}
	} else {
		return nil, errors.New("错误的用户类型")
	}

	return funcMenuLists, nil
}
