198 lines
6.9 KiB
TypeScript
198 lines
6.9 KiB
TypeScript
import { prisma } from '@/lib/prisma'
|
||
import type { StoreAccountFilterKey, StoreAccountFilters, StoreAccountFieldPermissionUpdate, StoreAccountType } from '@/lib/store-account-management'
|
||
|
||
export type StoreAccountRecord = {
|
||
id: string
|
||
storeName: string
|
||
operationOwner: string
|
||
operationDepartmentHead: string
|
||
operationDirector: string
|
||
profitCenter: string
|
||
accountType: string
|
||
account: string
|
||
passwordCiphertext: string
|
||
boundPhone: string
|
||
verificationContact: string
|
||
jstOrderService: string
|
||
accountGroupName: string
|
||
createdAt: Date
|
||
updatedAt: Date
|
||
}
|
||
|
||
export type StoreAccountChangeRecord = {
|
||
id: string
|
||
storeAccountId: string | null
|
||
storeName: string
|
||
operation: string
|
||
actorId: string | null
|
||
actorName: string
|
||
changes: unknown
|
||
createdAt: Date
|
||
}
|
||
|
||
export type StoreAccountFieldPermissionRecord = {
|
||
fieldKey: string
|
||
subjectKey: string
|
||
visibility: string
|
||
}
|
||
|
||
const ACCOUNT_SELECT = {
|
||
id: true,
|
||
storeName: true,
|
||
operationOwner: true,
|
||
operationDepartmentHead: true,
|
||
operationDirector: true,
|
||
profitCenter: true,
|
||
accountType: true,
|
||
account: true,
|
||
passwordCiphertext: true,
|
||
boundPhone: true,
|
||
verificationContact: true,
|
||
jstOrderService: true,
|
||
accountGroupName: true,
|
||
createdAt: true,
|
||
updatedAt: true,
|
||
} as const
|
||
|
||
function contains(value: string) {
|
||
return { contains: value, mode: 'insensitive' as const }
|
||
}
|
||
|
||
function splitStoreNames(value: string) {
|
||
return [...new Set(value.split(/[,,;;]+/).map((item) => item.trim()).filter(Boolean))].slice(0, 500)
|
||
}
|
||
|
||
function buildWhere(filters: StoreAccountFilters) {
|
||
const and: Array<Record<string, unknown>> = []
|
||
if (filters.search) {
|
||
const searchTokens = [...new Set(filters.search.split(/[\s,,;;]+/).map((value) => value.trim()).filter(Boolean))].slice(0, 100)
|
||
and.push({ OR: searchTokens.flatMap((token) => [
|
||
{ storeName: contains(token) },
|
||
{ account: contains(token) },
|
||
{ operationOwner: contains(token) },
|
||
{ operationDepartmentHead: contains(token) },
|
||
{ operationDirector: contains(token) },
|
||
{ profitCenter: contains(token) },
|
||
{ boundPhone: contains(token) },
|
||
{ verificationContact: contains(token) },
|
||
{ jstOrderService: contains(token) },
|
||
{ accountGroupName: contains(token) },
|
||
]) })
|
||
}
|
||
const map: Partial<Record<StoreAccountFilterKey, string>> = {
|
||
accountType: 'accountType',
|
||
operationOwner: 'operationOwner',
|
||
accountGroupName: 'accountGroupName',
|
||
}
|
||
const selectedStoreNames = splitStoreNames(filters.storeName)
|
||
if (selectedStoreNames.length > 0) and.push({ storeName: { in: selectedStoreNames } })
|
||
for (const [key, field] of Object.entries(map)) {
|
||
const value = filters[key as StoreAccountFilterKey]
|
||
if (value) and.push({ [field]: contains(value) })
|
||
}
|
||
return and.length ? { AND: and } : undefined
|
||
}
|
||
|
||
export class StoreAccountManagementRepository {
|
||
async listPage(params: { skip: number; take: number; filters: StoreAccountFilters }) {
|
||
const where = buildWhere(params.filters)
|
||
const [rows, total] = await Promise.all([
|
||
prisma.storeAccount.findMany({ where, select: ACCOUNT_SELECT, orderBy: [{ storeName: 'asc' }, { account: 'asc' }], skip: params.skip, take: params.take }),
|
||
prisma.storeAccount.count({ where }),
|
||
])
|
||
return { rows, total }
|
||
}
|
||
|
||
async listStoreNames() {
|
||
const rows = await prisma.storeAccount.findMany({
|
||
select: { storeName: true },
|
||
distinct: ['storeName'],
|
||
orderBy: { storeName: 'asc' },
|
||
})
|
||
return rows.map((row) => row.storeName.trim()).filter(Boolean)
|
||
}
|
||
|
||
async findById(id: string) {
|
||
return prisma.storeAccount.findUnique({ where: { id }, select: ACCOUNT_SELECT })
|
||
}
|
||
|
||
async findByKey(params: { storeName: string; accountType: StoreAccountType; account: string }) {
|
||
return prisma.storeAccount.findUnique({
|
||
where: { storeName_accountType_account: params },
|
||
select: ACCOUNT_SELECT,
|
||
})
|
||
}
|
||
|
||
async create(data: {
|
||
storeName: string
|
||
operationOwner?: string
|
||
operationDepartmentHead?: string
|
||
operationDirector?: string
|
||
profitCenter?: string
|
||
accountType: StoreAccountType
|
||
account: string
|
||
passwordCiphertext?: string
|
||
boundPhone?: string
|
||
verificationContact?: string
|
||
jstOrderService?: string
|
||
accountGroupName?: string
|
||
actorId: string
|
||
}) {
|
||
const { actorId, ...values } = data
|
||
return prisma.storeAccount.create({ data: { ...values, passwordCiphertext: values.passwordCiphertext ?? '', createdById: actorId, updatedById: actorId }, select: ACCOUNT_SELECT })
|
||
}
|
||
|
||
async update(id: string, data: Record<string, string> & { updatedById: string }) {
|
||
return prisma.storeAccount.update({ where: { id }, data, select: ACCOUNT_SELECT })
|
||
}
|
||
|
||
async listByIds(ids: string[]) {
|
||
return prisma.storeAccount.findMany({ where: { id: { in: ids } }, select: ACCOUNT_SELECT })
|
||
}
|
||
|
||
async upsertByKey(data: {
|
||
storeName: string
|
||
accountType: StoreAccountType
|
||
account: string
|
||
values: Record<string, string>
|
||
actorId: string
|
||
}) {
|
||
return prisma.storeAccount.upsert({
|
||
where: { storeName_accountType_account: { storeName: data.storeName, accountType: data.accountType, account: data.account } },
|
||
create: { ...data.values, storeName: data.storeName, accountType: data.accountType, account: data.account, createdById: data.actorId, updatedById: data.actorId },
|
||
update: { ...data.values, updatedById: data.actorId },
|
||
select: ACCOUNT_SELECT,
|
||
})
|
||
}
|
||
|
||
async createLog(data: { storeAccountId: string | null; storeName: string; operation: string; actorId: string; actorName: string; changes: unknown }) {
|
||
return prisma.storeAccountChangeLog.create({ data: { ...data, changes: data.changes as object } })
|
||
}
|
||
|
||
async listLogs(params: { storeAccountId?: string; storeName?: string; take: number }) {
|
||
return prisma.storeAccountChangeLog.findMany({
|
||
where: params.storeAccountId ? { storeAccountId: params.storeAccountId } : params.storeName ? { storeName: params.storeName } : undefined,
|
||
orderBy: { createdAt: 'desc' },
|
||
take: params.take,
|
||
})
|
||
}
|
||
|
||
async listFieldPermissions() {
|
||
return prisma.storeAccountFieldPermission.findMany({ select: { fieldKey: true, subjectKey: true, visibility: true } })
|
||
}
|
||
|
||
async saveFieldPermissions(params: { updates: StoreAccountFieldPermissionUpdate[]; updatedById: string }) {
|
||
return prisma.$transaction(params.updates.map((item) => item.visibility === null
|
||
? prisma.storeAccountFieldPermission.deleteMany({
|
||
where: { fieldKey: item.fieldKey, subjectKey: item.subjectKey },
|
||
})
|
||
: prisma.storeAccountFieldPermission.upsert({
|
||
where: { fieldKey_subjectKey: { fieldKey: item.fieldKey, subjectKey: item.subjectKey } },
|
||
create: { ...item, visibility: item.visibility, updatedById: params.updatedById },
|
||
update: { visibility: item.visibility, updatedById: params.updatedById },
|
||
})))
|
||
}
|
||
}
|
||
|
||
export const storeAccountManagementRepository = new StoreAccountManagementRepository()
|