feat(fund): 备用金账户编号改为带用户名的可读编号

This commit is contained in:
yml2213
2026-10-23 11:57:39 +08:00
parent a7f60414a4
commit fac34c8420
6 changed files with 73 additions and 7 deletions
@@ -0,0 +1,35 @@
-- 113_fund_account_no_readable.sql —— 备用金账户编号改为可读的 FA-<用户名>。
--
-- 背景:历史编号是 FA + 10 位随机十六进制(如 FA7683D6B58B),
-- 在「备用金」列表里无法一眼对应到具体客服。
-- 新规则:FA-<大写用户名>,例如 FA-KF_2、FA-JIAJIALI;
-- 同一用户名重名时追加 -2、-3。
WITH base AS (
SELECT
fa.id,
'FA-' || COALESCE(
NULLIF(
trim(
both '_' FROM upper(
regexp_replace(COALESCE(NULLIF(au.username, ''), 'account-' || fa.id), '[^A-Za-z0-9_]+', '_', 'g')
)
),
''
),
'ACCOUNT-' || fa.id
) AS base_no
FROM fund_accounts fa
LEFT JOIN admin_users au ON au.id = fa.owner_admin_user_id
),
numbered AS (
SELECT
id,
base_no,
row_number() OVER (PARTITION BY base_no ORDER BY id) AS rn
FROM base
)
UPDATE fund_accounts fa
SET account_no = CASE WHEN n.rn = 1 THEN n.base_no ELSE n.base_no || '-' || n.rn END
FROM numbered n
WHERE fa.id = n.id;
@@ -278,6 +278,37 @@ function generateNo(prefix: string) {
return randomId(prefix, 5).toUpperCase()
}
function buildFundAccountNoBase(username: string): string {
const slug =
String(username || '')
.trim()
.replace(/[^A-Za-z0-9_]+/g, '_')
.replace(/^_+|_+$/g, '')
.toUpperCase()
.slice(0, 32) || 'ACCOUNT'
return `FA-${slug}`
}
/** 账户编号直接带用户名,列表里一眼能对应到人;重名时追加 -2、-3。 */
async function allocateFundAccountNo(client: PoolClient, username: string): Promise<string> {
const base = buildFundAccountNoBase(username)
let candidate = base
for (let suffix = 2; suffix <= 99; suffix += 1) {
const existing = await client.query(
'SELECT 1 FROM fund_accounts WHERE account_no = $1 LIMIT 1',
[candidate],
)
if (!existing.rowCount) {
return candidate
}
candidate = `${base}-${suffix}`
}
throw createHttpError('生成备用金账户编号失败', {
statusCode: 500,
errorCode: 'fund_account_no_conflict',
})
}
function requiredRow<T>(row: T | undefined, message: string): T {
if (!row) {
throw createHttpError(message, {
@@ -974,7 +1005,7 @@ export async function createAdminFundAccount(payload: JsonObject, session: Admin
})
}
const accountNo = generateNo('FA')
const accountNo = await allocateFundAccountNo(client, user.username)
const insertResult = await client.query<FundAccountRow>(
`
INSERT INTO fund_accounts (
@@ -1062,7 +1093,7 @@ export async function ensureAdminFundAgentAccounts(payload: JsonObject, session:
const created: Array<{ id: number; accountNo: string; ownerName: string }> = []
for (const user of usersResult.rows) {
const accountNo = generateNo('FA')
const accountNo = await allocateFundAccountNo(client, user.username)
const insertResult = await client.query<FundAccountRow>(
`
INSERT INTO fund_accounts (