547 lines
16 KiB
Go
547 lines
16 KiB
Go
package iot
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/encoding/gjson"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
)
|
|
|
|
type stockBusinessFilter struct {
|
|
ChanName string
|
|
CardType string
|
|
UpPkgKeyword string
|
|
AgreementExpiryMonths int
|
|
}
|
|
|
|
type stockBatchBusinessFields struct {
|
|
ChanKey string
|
|
CardType string
|
|
UpPkgCode string
|
|
UpPkgName string
|
|
AgreementExpiryDate string
|
|
}
|
|
|
|
func applyStockBusinessFilters(ctx context.Context, mod *gdb.Model, filter stockBusinessFilter) *gdb.Model {
|
|
if mod == nil {
|
|
return mod
|
|
}
|
|
|
|
stockTable := dao.TSimCardStock.Table()
|
|
stockColumns := dao.TSimCardStock.Columns()
|
|
|
|
if value := strings.TrimSpace(filter.ChanName); value != "" {
|
|
channelColumns := dao.TSimChannels.Columns()
|
|
like := "%" + value + "%"
|
|
mod = mod.Where(
|
|
fmt.Sprintf(
|
|
"EXISTS (SELECT 1 FROM `%s` c WHERE c.`%s` = `%s`.`%s` AND c.`%s` = ? AND (c.`%s` LIKE ? OR c.`%s` LIKE ?))",
|
|
dao.TSimChannels.Table(),
|
|
channelColumns.ChanKey,
|
|
stockTable,
|
|
stockColumns.ChanKey,
|
|
channelColumns.TenantId,
|
|
channelDisplayNameColumn(ctx),
|
|
channelColumns.ChanKey,
|
|
),
|
|
contexts.GetTenantId(ctx),
|
|
like,
|
|
like,
|
|
)
|
|
}
|
|
|
|
if value := strings.TrimSpace(filter.CardType); value != "" {
|
|
cardTypeExpr := batchInfoTextSQL("bm.`"+dao.BatchMain.Columns().BatchInfo+"`", "cardType")
|
|
mod = mod.Where(stockInboundBatchExistsSQL(cardTypeExpr+" = ?"), consts.BatchTypeInbound, value)
|
|
}
|
|
|
|
if value := strings.TrimSpace(filter.UpPkgKeyword); value != "" {
|
|
upstreamColumns := dao.TUpstreamPackage.Columns()
|
|
upPkgExpr := batchInfoTextSQL("bm.`"+dao.BatchMain.Columns().BatchInfo+"`", "upPkgCode")
|
|
like := "%" + value + "%"
|
|
mod = mod.Where(
|
|
stockInboundBatchJoinExistsSQL(
|
|
fmt.Sprintf("LEFT JOIN `%s` up ON up.`%s` = %s", dao.TUpstreamPackage.Table(), upstreamColumns.UpPkgCode, upPkgExpr),
|
|
fmt.Sprintf("(up.`%s` LIKE ? OR %s LIKE ?)", upstreamColumns.UpPkgName, upPkgExpr),
|
|
),
|
|
consts.BatchTypeInbound,
|
|
like,
|
|
like,
|
|
)
|
|
}
|
|
|
|
if filter.AgreementExpiryMonths > 0 {
|
|
months := filter.AgreementExpiryMonths
|
|
if months != 1 && months != 3 && months != 6 {
|
|
months = 6
|
|
}
|
|
now := time.Now()
|
|
start := now.Format("2006-01-02")
|
|
end := now.AddDate(0, months, 0).Format("2006-01-02")
|
|
agreementExpr := batchInfoTextSQL("bm.`"+dao.BatchMain.Columns().BatchInfo+"`", "agreementExpiryDate")
|
|
mod = mod.Where(
|
|
stockInboundBatchExistsSQL("DATE("+agreementExpr+") BETWEEN ? AND ?"),
|
|
consts.BatchTypeInbound,
|
|
start,
|
|
end,
|
|
)
|
|
}
|
|
|
|
return mod
|
|
}
|
|
|
|
func stockInboundBatchExistsSQL(extra string) string {
|
|
return stockInboundBatchJoinExistsSQL("", extra)
|
|
}
|
|
|
|
func stockInboundBatchJoinExistsSQL(joinSQL, extra string) string {
|
|
stockTable := dao.TSimCardStock.Table()
|
|
stockColumns := dao.TSimCardStock.Columns()
|
|
batchColumns := dao.BatchMain.Columns()
|
|
joinPart := ""
|
|
if strings.TrimSpace(joinSQL) != "" {
|
|
joinPart = " " + joinSQL
|
|
}
|
|
return fmt.Sprintf(
|
|
"EXISTS (SELECT 1 FROM `%s` bm%s WHERE bm.`%s` = `%s`.`%s` AND bm.`%s` = `%s`.`%s` AND bm.`%s` = ? AND %s)",
|
|
dao.BatchMain.Table(),
|
|
joinPart,
|
|
batchColumns.BatchNo,
|
|
stockTable,
|
|
stockColumns.BatchNo,
|
|
batchColumns.TenantId,
|
|
stockTable,
|
|
stockColumns.TenantId,
|
|
batchColumns.BatchType,
|
|
extra,
|
|
)
|
|
}
|
|
|
|
func batchInfoTextSQL(columnExpr, field string) string {
|
|
return fmt.Sprintf(
|
|
"COALESCE(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(%s, '$.inboundOptions.%s')), ''), JSON_UNQUOTE(JSON_EXTRACT(%s, '$.%s')))",
|
|
columnExpr,
|
|
field,
|
|
columnExpr,
|
|
field,
|
|
)
|
|
}
|
|
|
|
func enrichStockListBusinessFields(ctx context.Context, list []*model.TSimCardStockListModel) error {
|
|
if len(list) == 0 {
|
|
return nil
|
|
}
|
|
|
|
batchNos := make([]string, 0, len(list))
|
|
chanKeys := make([]string, 0, len(list))
|
|
deptIds := make([]int64, 0, len(list))
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
batchNos = appendUniqueNonBlank(batchNos, item.BatchNo)
|
|
chanKeys = appendUniqueNonBlank(chanKeys, item.ChanKey)
|
|
if item.CreatedDept > 0 {
|
|
deptIds = append(deptIds, item.CreatedDept)
|
|
}
|
|
}
|
|
|
|
batchMap, err := loadStockBatchBusinessFields(ctx, batchNos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
detailMap, err := loadStockDetailBusinessFields(ctx, batchNos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, fields := range detailMap {
|
|
chanKeys = appendUniqueNonBlank(chanKeys, fields.ChanKey)
|
|
}
|
|
channelMap, err := loadStockChannelNames(ctx, chanKeys)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
deptMap, err := loadStockDeptNames(ctx, deptIds)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
fields := mergeStockBusinessFields(batchMap[item.BatchNo], detailMap[item.BatchNo])
|
|
item.ChanName = channelMap[firstNonBlankString(item.ChanKey, fields.ChanKey)]
|
|
item.CardType = fields.CardType
|
|
item.UpPkgCode = fields.UpPkgCode
|
|
item.UpPkgName = fields.UpPkgName
|
|
item.AgreementExpiryDate = fields.AgreementExpiryDate
|
|
item.CreatedDeptName = deptMap[item.CreatedDept]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func enrichOutboundBatchBusinessFields(ctx context.Context, list []*model.TSimCardOutboundInboundBatchListItem) error {
|
|
if len(list) == 0 {
|
|
return nil
|
|
}
|
|
|
|
batchNos := make([]string, 0, len(list))
|
|
chanKeys := make([]string, 0, len(list))
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
batchNos = appendUniqueNonBlank(batchNos, item.BatchNo)
|
|
chanKeys = appendUniqueNonBlank(chanKeys, item.ChanKey)
|
|
}
|
|
|
|
batchMap, err := loadStockBatchBusinessFields(ctx, batchNos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
detailMap, err := loadStockDetailBusinessFields(ctx, batchNos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, fields := range detailMap {
|
|
chanKeys = appendUniqueNonBlank(chanKeys, fields.ChanKey)
|
|
}
|
|
channelMap, err := loadStockChannelNames(ctx, chanKeys)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
fields := mergeStockBusinessFields(batchMap[item.BatchNo], detailMap[item.BatchNo])
|
|
item.ChanName = channelMap[firstNonBlankString(item.ChanKey, fields.ChanKey)]
|
|
item.CardType = fields.CardType
|
|
item.UpPkgCode = fields.UpPkgCode
|
|
item.UpPkgName = fields.UpPkgName
|
|
item.AgreementExpiryDate = fields.AgreementExpiryDate
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func enrichOutboundBatchCardBusinessFields(ctx context.Context, list []*model.TSimCardOutboundInboundBatchCardItem) error {
|
|
if len(list) == 0 {
|
|
return nil
|
|
}
|
|
|
|
batchNos := make([]string, 0, len(list))
|
|
chanKeys := make([]string, 0, len(list))
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
batchNos = appendUniqueNonBlank(batchNos, item.BatchNo)
|
|
chanKeys = appendUniqueNonBlank(chanKeys, item.ChanKey)
|
|
}
|
|
|
|
batchMap, err := loadStockBatchBusinessFields(ctx, batchNos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
detailMap, err := loadStockDetailBusinessFields(ctx, batchNos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, fields := range detailMap {
|
|
chanKeys = appendUniqueNonBlank(chanKeys, fields.ChanKey)
|
|
}
|
|
channelMap, err := loadStockChannelNames(ctx, chanKeys)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
fields := mergeStockBusinessFields(batchMap[item.BatchNo], detailMap[item.BatchNo])
|
|
item.ChanName = channelMap[firstNonBlankString(item.ChanKey, fields.ChanKey)]
|
|
item.CardType = fields.CardType
|
|
item.UpPkgCode = fields.UpPkgCode
|
|
item.UpPkgName = fields.UpPkgName
|
|
item.AgreementExpiryDate = fields.AgreementExpiryDate
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func loadStockBatchBusinessFields(ctx context.Context, batchNos []string) (map[string]stockBatchBusinessFields, error) {
|
|
result := make(map[string]stockBatchBusinessFields)
|
|
batchNos = uniqueNonBlank(batchNos)
|
|
if len(batchNos) == 0 {
|
|
return result, nil
|
|
}
|
|
|
|
var batches []*entity.BatchMain
|
|
columns := dao.BatchMain.Columns()
|
|
if err := dao.BatchMain.Ctx(ctx).
|
|
Fields(columns.BatchNo, columns.BatchInfo).
|
|
Where(columns.TenantId, contexts.GetTenantId(ctx)).
|
|
Where(columns.BatchType, consts.BatchTypeInbound).
|
|
WhereIn(columns.BatchNo, batchNos).
|
|
Scan(&batches); err != nil {
|
|
return nil, gerror.Wrap(err, "补充入库批次业务字段失败")
|
|
}
|
|
|
|
upPkgCodes := make([]string, 0, len(batches))
|
|
for _, batch := range batches {
|
|
if batch == nil {
|
|
continue
|
|
}
|
|
fields := stockBatchBusinessFields{
|
|
ChanKey: batchInfoString(batch.BatchInfo, "chanKey"),
|
|
CardType: batchInfoString(batch.BatchInfo, "cardType"),
|
|
UpPkgCode: batchInfoString(batch.BatchInfo, "upPkgCode"),
|
|
AgreementExpiryDate: batchInfoString(batch.BatchInfo, "agreementExpiryDate"),
|
|
}
|
|
result[batch.BatchNo] = fields
|
|
upPkgCodes = appendUniqueNonBlank(upPkgCodes, fields.UpPkgCode)
|
|
}
|
|
|
|
upPkgNames, err := loadStockUpstreamPackageNames(ctx, upPkgCodes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for batchNo, fields := range result {
|
|
fields.UpPkgName = upPkgNames[fields.UpPkgCode]
|
|
if fields.UpPkgName == "" {
|
|
fields.UpPkgName = fields.UpPkgCode
|
|
}
|
|
result[batchNo] = fields
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func loadStockDetailBusinessFields(ctx context.Context, batchNos []string) (map[string]stockBatchBusinessFields, error) {
|
|
result := make(map[string]stockBatchBusinessFields)
|
|
batchNos = uniqueNonBlank(batchNos)
|
|
if len(batchNos) == 0 {
|
|
return result, nil
|
|
}
|
|
|
|
var details []*entity.BatchDetail
|
|
columns := dao.BatchDetail.Columns()
|
|
if err := dao.BatchDetail.Ctx(ctx).
|
|
Fields(columns.BatchNo, columns.Snapshot).
|
|
Where(columns.TenantId, contexts.GetTenantId(ctx)).
|
|
Where(columns.BatchType, consts.BatchTypeInbound).
|
|
Where(columns.DetailStatus, consts.BatchDetailStatusSuccess).
|
|
WhereIn(columns.BatchNo, batchNos).
|
|
OrderAsc(columns.RowNo).
|
|
Scan(&details); err != nil {
|
|
return nil, gerror.Wrap(err, "load inbound batch detail business fields failed")
|
|
}
|
|
|
|
upPkgCodes := make([]string, 0, len(details))
|
|
for _, detail := range details {
|
|
if detail == nil || detail.Snapshot == nil {
|
|
continue
|
|
}
|
|
current := result[detail.BatchNo]
|
|
fields := stockSnapshotBusinessFields(detail.Snapshot)
|
|
current = mergeStockBusinessFields(current, fields)
|
|
result[detail.BatchNo] = current
|
|
upPkgCodes = appendUniqueNonBlank(upPkgCodes, current.UpPkgCode)
|
|
}
|
|
|
|
upPkgNames, err := loadStockUpstreamPackageNames(ctx, upPkgCodes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for batchNo, fields := range result {
|
|
fields.UpPkgName = upPkgNames[fields.UpPkgCode]
|
|
if fields.UpPkgName == "" {
|
|
fields.UpPkgName = fields.UpPkgCode
|
|
}
|
|
result[batchNo] = fields
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func stockSnapshotBusinessFields(snapshot *gjson.Json) stockBatchBusinessFields {
|
|
if snapshot == nil {
|
|
return stockBatchBusinessFields{}
|
|
}
|
|
return stockBatchBusinessFields{
|
|
ChanKey: snapshotString(snapshot, "chanKey", "rawFields.chanKey"),
|
|
CardType: snapshotString(snapshot, "cardType", "rawFields.cardType"),
|
|
UpPkgCode: snapshotString(snapshot, "upPkgCode", "rawFields.upPkgCode"),
|
|
AgreementExpiryDate: snapshotString(snapshot, "agreementExpiryDate", "rawFields.agreementExpiryDate"),
|
|
}
|
|
}
|
|
|
|
func snapshotString(snapshot *gjson.Json, paths ...string) string {
|
|
for _, path := range paths {
|
|
if value := strings.TrimSpace(snapshot.Get(path).String()); value != "" {
|
|
return value
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func mergeStockBusinessFields(primary, fallback stockBatchBusinessFields) stockBatchBusinessFields {
|
|
return stockBatchBusinessFields{
|
|
ChanKey: firstNonBlankString(primary.ChanKey, fallback.ChanKey),
|
|
CardType: firstNonBlankString(primary.CardType, fallback.CardType),
|
|
UpPkgCode: firstNonBlankString(primary.UpPkgCode, fallback.UpPkgCode),
|
|
UpPkgName: firstNonBlankString(primary.UpPkgName, fallback.UpPkgName),
|
|
AgreementExpiryDate: firstNonBlankString(primary.AgreementExpiryDate, fallback.AgreementExpiryDate),
|
|
}
|
|
}
|
|
|
|
func firstNonBlankString(values ...string) string {
|
|
for _, value := range values {
|
|
if trimmed := strings.TrimSpace(value); trimmed != "" {
|
|
return trimmed
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func loadStockChannelNames(ctx context.Context, chanKeys []string) (map[string]string, error) {
|
|
result := make(map[string]string)
|
|
chanKeys = uniqueNonBlank(chanKeys)
|
|
if len(chanKeys) == 0 {
|
|
return result, nil
|
|
}
|
|
|
|
type channelRow struct {
|
|
ChanKey string `orm:"chan_key"`
|
|
ChanName string `orm:"chan_name"`
|
|
}
|
|
var rows []*channelRow
|
|
columns := dao.TSimChannels.Columns()
|
|
if err := dao.TSimChannels.Ctx(ctx).
|
|
Fields(columns.ChanKey, channelDisplayNameSQL(ctx, dao.TSimChannels.Table())+" AS chan_name").
|
|
Where(columns.TenantId, contexts.GetTenantId(ctx)).
|
|
WhereIn(columns.ChanKey, chanKeys).
|
|
Scan(&rows); err != nil {
|
|
return nil, gerror.Wrap(err, "补充通道名称失败")
|
|
}
|
|
for _, row := range rows {
|
|
if row != nil {
|
|
result[row.ChanKey] = row.ChanName
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// loadStockDeptNames 批量补充创建部门名称(sys_dept.dept_id -> dept_name)。
|
|
func loadStockDeptNames(ctx context.Context, deptIds []int64) (map[int64]string, error) {
|
|
result := make(map[int64]string)
|
|
deptIds = uniqueInt64(deptIds)
|
|
if len(deptIds) == 0 {
|
|
return result, nil
|
|
}
|
|
|
|
type deptRow struct {
|
|
DeptId int64 `orm:"dept_id"`
|
|
DeptName string `orm:"dept_name"`
|
|
}
|
|
var rows []*deptRow
|
|
columns := dao.SysDept.Columns()
|
|
mod := dao.SysDept.Ctx(ctx).
|
|
Fields(columns.DeptId, columns.DeptName).
|
|
WhereIn(columns.DeptId, deptIds)
|
|
if tenantId := strings.TrimSpace(contexts.GetTenantId(ctx)); tenantId != "" {
|
|
mod = mod.Where(columns.TenantId, tenantId)
|
|
}
|
|
if err := mod.Scan(&rows); err != nil {
|
|
return nil, gerror.Wrap(err, "补充创建部门名称失败")
|
|
}
|
|
for _, row := range rows {
|
|
if row != nil && row.DeptId > 0 {
|
|
result[row.DeptId] = row.DeptName
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// uniqueInt64 去重并剔除非正的部门ID。
|
|
func uniqueInt64(values []int64) []int64 {
|
|
seen := make(map[int64]bool, len(values))
|
|
result := make([]int64, 0, len(values))
|
|
for _, value := range values {
|
|
if value <= 0 || seen[value] {
|
|
continue
|
|
}
|
|
seen[value] = true
|
|
result = append(result, value)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func loadStockUpstreamPackageNames(ctx context.Context, upPkgCodes []string) (map[string]string, error) {
|
|
result := make(map[string]string)
|
|
upPkgCodes = uniqueNonBlank(upPkgCodes)
|
|
if len(upPkgCodes) == 0 {
|
|
return result, nil
|
|
}
|
|
|
|
type upstreamPackageRow struct {
|
|
UpPkgCode string `orm:"up_pkg_code"`
|
|
UpPkgName string `orm:"up_pkg_name"`
|
|
}
|
|
var rows []*upstreamPackageRow
|
|
columns := dao.TUpstreamPackage.Columns()
|
|
if err := dao.TUpstreamPackage.Ctx(ctx).
|
|
Fields(columns.UpPkgCode, columns.UpPkgName).
|
|
WhereIn(columns.UpPkgCode, upPkgCodes).
|
|
Scan(&rows); err != nil {
|
|
return nil, gerror.Wrap(err, "补充上游套餐名称失败")
|
|
}
|
|
for _, row := range rows {
|
|
if row != nil {
|
|
result[row.UpPkgCode] = row.UpPkgName
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func batchInfoString(info *gjson.Json, field string) string {
|
|
if info == nil {
|
|
return ""
|
|
}
|
|
if value := strings.TrimSpace(info.Get("inboundOptions." + field).String()); value != "" {
|
|
return value
|
|
}
|
|
return strings.TrimSpace(info.Get(field).String())
|
|
}
|
|
|
|
func appendUniqueNonBlank(values []string, value string) []string {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return values
|
|
}
|
|
for _, item := range values {
|
|
if item == value {
|
|
return values
|
|
}
|
|
}
|
|
return append(values, value)
|
|
}
|
|
|
|
func uniqueNonBlank(values []string) []string {
|
|
result := make([]string, 0, len(values))
|
|
for _, value := range values {
|
|
result = appendUniqueNonBlank(result, value)
|
|
}
|
|
return result
|
|
}
|