Remove backup/env files from tracking, update .gitignore
This commit is contained in:
@@ -1,12 +0,0 @@
|
|||||||
PG_HOST=localhost
|
|
||||||
PG_PORT=5432
|
|
||||||
PG_DATABASE=omniai
|
|
||||||
PG_USER=omniai
|
|
||||||
PG_PASSWORD=bybyby@123
|
|
||||||
PG_POOL_MAX=10
|
|
||||||
PORT=3600
|
|
||||||
HOST=0.0.0.0
|
|
||||||
JWT_SECRET=499808ef76791e59ab1019f8fbb86d2b
|
|
||||||
DEFAULT_ADMIN_PASSWORD=bybyby@123BY
|
|
||||||
JWT_EXPIRES_IN=7d
|
|
||||||
CORS_ORIGINS=*
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
PG_HOST=localhost
|
|
||||||
PG_PORT=5432
|
|
||||||
PG_DATABASE=omniai
|
|
||||||
PG_USER=omniai
|
|
||||||
PG_PASSWORD=bybyby@123
|
|
||||||
PG_POOL_MAX=10
|
|
||||||
PORT=3600
|
|
||||||
HOST=0.0.0.0
|
|
||||||
JWT_SECRET=499808ef76791e59ab1019f8fbb86d2b
|
|
||||||
DEFAULT_ADMIN_PASSWORD=bybyby@123BY
|
|
||||||
JWT_EXPIRES_IN=7d
|
|
||||||
CORS_ORIGINS=*
|
|
||||||
STS_ACCESS_KEY_ID=LTAI5t7qL3iR9dchydHQ3cmT
|
|
||||||
STS_ACCESS_KEY_SECRET=ssywO1bUwu2pPZaq3KugXbaE0Za9gi
|
|
||||||
OSS_ROLE_ARN=acs:ram::1582660594690998:role/omniai-oss-role
|
|
||||||
OSS_BUCKET=stringtest
|
|
||||||
OSS_REGION=oss-cn-hangzhou
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
PG_HOST=localhost
|
|
||||||
PG_PORT=5432
|
|
||||||
PG_DATABASE=omniai
|
|
||||||
PG_USER=omniai
|
|
||||||
PG_PASSWORD=bybyby@123
|
|
||||||
PG_POOL_MAX=10
|
|
||||||
PORT=3600
|
|
||||||
HOST=0.0.0.0
|
|
||||||
JWT_SECRET=499808ef76791e59ab1019f8fbb86d2b
|
|
||||||
DEFAULT_ADMIN_PASSWORD=bybyby@123BY
|
|
||||||
JWT_EXPIRES_IN=7d
|
|
||||||
CORS_ORIGINS=*
|
|
||||||
STS_ACCESS_KEY_ID=LTAI5t7qL3iR9dchydHQ3cmT
|
|
||||||
STS_ACCESS_KEY_SECRET=ssywO1bUwu2pPZaq3KugXbaE0Za9gi
|
|
||||||
OSS_ROLE_ARN=acs:ram::1582660594690998:role/OmniAI-OSS-Upload
|
|
||||||
OSS_BUCKET=stringtest
|
|
||||||
OSS_REGION=oss-cn-hangzhou
|
|
||||||
@@ -8,3 +8,8 @@ config/internal-beta-codes.md
|
|||||||
data/
|
data/
|
||||||
*.tmp
|
*.tmp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
*.env.backup*
|
||||||
|
*.env.rolefix*
|
||||||
|
*.env.enterprise*
|
||||||
|
src/routes/ai.js.bak*
|
||||||
|
src/routes/enterprise.js.enterprise-usage-fix*
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,456 +0,0 @@
|
|||||||
const {
|
|
||||||
requireAuth,
|
|
||||||
requireEnterpriseAdmin,
|
|
||||||
distributeCredits,
|
|
||||||
getEnterpriseFinancials,
|
|
||||||
getEnterpriseName,
|
|
||||||
pool,
|
|
||||||
getPeriodStart,
|
|
||||||
generateOrderNo,
|
|
||||||
clampPositiveInteger,
|
|
||||||
clampNonNegativeInteger,
|
|
||||||
} = require("./context");
|
|
||||||
|
|
||||||
function registerEnterpriseRoutes(router) {
|
|
||||||
// ── Enterprise: Dashboard & Financials ────────────────────────────────
|
|
||||||
|
|
||||||
router.get("/enterprise/dashboard", requireAuth, requireEnterpriseAdmin, async (req, res) => {
|
|
||||||
const financials = await getEnterpriseFinancials(req.user.enterpriseId);
|
|
||||||
const {
|
|
||||||
rows: [countRow],
|
|
||||||
} = await pool.query(
|
|
||||||
"SELECT COUNT(*) AS count FROM users WHERE enterprise_id = $1 AND enabled = 1 AND is_enterprise_admin = 0",
|
|
||||||
[req.user.enterpriseId],
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
enterpriseName: req.user.enterpriseName,
|
|
||||||
enterpriseCode: req.user.enterpriseCode,
|
|
||||||
balanceCents: financials.balanceCents,
|
|
||||||
activePackages: financials.activePackages,
|
|
||||||
subAccountCount: Number(countRow?.count || 0),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/enterprise/financials", requireAuth, requireEnterpriseAdmin, async (req, res) => {
|
|
||||||
const financials = await getEnterpriseFinancials(req.user.enterpriseId);
|
|
||||||
res.json({
|
|
||||||
balanceCents: financials.balanceCents,
|
|
||||||
activePackages: financials.activePackages.map((p) => ({
|
|
||||||
id: p.id,
|
|
||||||
packageName: p.package_name,
|
|
||||||
remainingImage: p.remaining_image,
|
|
||||||
remainingVideo: p.remaining_video,
|
|
||||||
remainingText: p.remaining_text,
|
|
||||||
expiresAt: p.expires_at,
|
|
||||||
activatedAt: p.activated_at,
|
|
||||||
})),
|
|
||||||
recentTransactions: financials.recentTransactions.map((t) => ({
|
|
||||||
id: t.id,
|
|
||||||
type: t.type,
|
|
||||||
amountCents: t.amount_cents,
|
|
||||||
balanceAfterCents: t.balance_after_cents,
|
|
||||||
description: t.description,
|
|
||||||
createdAt: t.created_at,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/enterprise/usage/summary", requireAuth, requireEnterpriseAdmin, async (req, res) => {
|
|
||||||
const { period = "30d" } = req.query;
|
|
||||||
const periodStart = getPeriodStart(period);
|
|
||||||
const ledgerDateJoin = periodStart ? `AND cl.created_at >= ${periodStart}` : "";
|
|
||||||
const ledgerDateWhere = periodStart ? `AND created_at >= ${periodStart}` : "";
|
|
||||||
const recordsLimit = clampPositiveInteger(req.query.limit, 50, 200);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const {
|
|
||||||
rows: [enterprise],
|
|
||||||
} = await pool.query(
|
|
||||||
"SELECT id, name, balance_cents FROM enterprises WHERE id = $1 AND enabled = 1 LIMIT 1",
|
|
||||||
[req.user.enterpriseId],
|
|
||||||
);
|
|
||||||
if (!enterprise) return res.status(404).json({ error: "企业不存在或已禁用" });
|
|
||||||
|
|
||||||
const { rows: members } = await pool.query(
|
|
||||||
`
|
|
||||||
SELECT
|
|
||||||
u.id AS user_id,
|
|
||||||
u.username,
|
|
||||||
COALESCE(em.role, CASE WHEN u.is_enterprise_admin = 1 THEN 'admin' ELSE 'employee' END) AS role,
|
|
||||||
COALESCE(SUM(CASE WHEN cl.status IN ('reserved', 'charged') THEN cl.amount_cents ELSE 0 END), 0) AS used_cents,
|
|
||||||
COUNT(CASE WHEN cl.status IN ('reserved', 'charged') THEN 1 END) AS task_count,
|
|
||||||
MAX(cl.created_at) AS last_used_at
|
|
||||||
FROM users u
|
|
||||||
LEFT JOIN enterprise_members em ON em.enterprise_id = u.enterprise_id AND em.user_id = u.id
|
|
||||||
LEFT JOIN credit_ledger cl ON cl.enterprise_id = u.enterprise_id AND cl.user_id = u.id ${ledgerDateJoin}
|
|
||||||
WHERE u.enterprise_id = $1 AND u.enabled = 1
|
|
||||||
GROUP BY u.id, u.username, role
|
|
||||||
ORDER BY used_cents DESC, u.id ASC
|
|
||||||
`,
|
|
||||||
[req.user.enterpriseId],
|
|
||||||
);
|
|
||||||
|
|
||||||
const { rows: modelBreakdown } = await pool.query(
|
|
||||||
`
|
|
||||||
SELECT
|
|
||||||
COALESCE(model, 'unknown') AS model,
|
|
||||||
COALESCE(SUM(amount_cents), 0) AS used_cents,
|
|
||||||
COUNT(*) AS task_count
|
|
||||||
FROM credit_ledger
|
|
||||||
WHERE enterprise_id = $1
|
|
||||||
AND status IN ('reserved', 'charged')
|
|
||||||
${ledgerDateWhere}
|
|
||||||
GROUP BY COALESCE(model, 'unknown')
|
|
||||||
ORDER BY used_cents DESC
|
|
||||||
LIMIT 50
|
|
||||||
`,
|
|
||||||
[req.user.enterpriseId],
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
|
||||||
rows: [totalRow],
|
|
||||||
} = await pool.query(
|
|
||||||
`
|
|
||||||
SELECT COALESCE(SUM(amount_cents), 0) AS total_used_cents
|
|
||||||
FROM credit_ledger
|
|
||||||
WHERE enterprise_id = $1
|
|
||||||
AND status IN ('reserved', 'charged')
|
|
||||||
${ledgerDateWhere}
|
|
||||||
`,
|
|
||||||
[req.user.enterpriseId],
|
|
||||||
);
|
|
||||||
|
|
||||||
const { rows: records } = await pool.query(
|
|
||||||
`
|
|
||||||
SELECT cl.*, u.username
|
|
||||||
FROM credit_ledger cl
|
|
||||||
LEFT JOIN users u ON u.id = cl.user_id
|
|
||||||
WHERE cl.enterprise_id = $1
|
|
||||||
AND cl.status IN ('reserved', 'charged')
|
|
||||||
${periodStart ? `AND cl.created_at >= ${periodStart}` : ""}
|
|
||||||
ORDER BY cl.created_at DESC
|
|
||||||
LIMIT $2
|
|
||||||
`,
|
|
||||||
[req.user.enterpriseId, recordsLimit],
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
enterpriseId: String(enterprise.id),
|
|
||||||
enterpriseName: enterprise.name,
|
|
||||||
balanceCents: Number(enterprise.balance_cents || 0),
|
|
||||||
totalUsedCents: Number(totalRow?.total_used_cents || 0),
|
|
||||||
members: members.map((row) => ({
|
|
||||||
userId: Number(row.user_id),
|
|
||||||
username: row.username,
|
|
||||||
role: row.role || "employee",
|
|
||||||
usedCents: Number(row.used_cents || 0),
|
|
||||||
taskCount: Number(row.task_count || 0),
|
|
||||||
lastUsedAt: row.last_used_at || null,
|
|
||||||
})),
|
|
||||||
modelBreakdown: modelBreakdown.map((row) => ({
|
|
||||||
model: row.model,
|
|
||||||
usedCents: Number(row.used_cents || 0),
|
|
||||||
taskCount: Number(row.task_count || 0),
|
|
||||||
})),
|
|
||||||
records: records.map((row) => ({
|
|
||||||
id: String(row.id),
|
|
||||||
userId: row.user_id == null ? "" : Number(row.user_id),
|
|
||||||
username: row.username || "",
|
|
||||||
model: row.model || "",
|
|
||||||
taskType: row.task_type,
|
|
||||||
resolution: row.resolution || null,
|
|
||||||
durationSeconds: row.duration_seconds == null ? null : Number(row.duration_seconds),
|
|
||||||
amountCents: Number(row.amount_cents || 0),
|
|
||||||
status: row.status,
|
|
||||||
createdAt: row.created_at,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[enterprise/usage/summary] failed", error);
|
|
||||||
res.status(500).json({ error: "企业用量汇总加载失败" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/enterprise/usage/records", requireAuth, requireEnterpriseAdmin, async (req, res) => {
|
|
||||||
const limit = clampPositiveInteger(req.query.limit, 50, 200);
|
|
||||||
const offset = clampNonNegativeInteger(req.query.offset, 0, 100000);
|
|
||||||
const userId = req.query.userId || req.query.user_id;
|
|
||||||
const model = String(req.query.model || "").trim();
|
|
||||||
const dateFrom = req.query.from || req.query.date_from;
|
|
||||||
const dateTo = req.query.to || req.query.date_to;
|
|
||||||
const where = ["cl.enterprise_id = $1"];
|
|
||||||
const params = [req.user.enterpriseId];
|
|
||||||
|
|
||||||
if (userId) {
|
|
||||||
params.push(userId);
|
|
||||||
where.push(`cl.user_id = $${params.length}`);
|
|
||||||
}
|
|
||||||
if (model) {
|
|
||||||
params.push(model);
|
|
||||||
where.push(`cl.model = $${params.length}`);
|
|
||||||
}
|
|
||||||
if (dateFrom) {
|
|
||||||
params.push(`${dateFrom}T00:00:00.000Z`);
|
|
||||||
where.push(`cl.created_at >= $${params.length}`);
|
|
||||||
}
|
|
||||||
if (dateTo) {
|
|
||||||
params.push(`${dateTo}T23:59:59.999Z`);
|
|
||||||
where.push(`cl.created_at <= $${params.length}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const whereSql = `WHERE ${where.join(" AND ")}`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const {
|
|
||||||
rows: [countRow],
|
|
||||||
} = await pool.query(`SELECT COUNT(*) AS total FROM credit_ledger cl ${whereSql}`, params);
|
|
||||||
const { rows } = await pool.query(
|
|
||||||
`
|
|
||||||
SELECT cl.*, u.username
|
|
||||||
FROM credit_ledger cl
|
|
||||||
LEFT JOIN users u ON u.id = cl.user_id
|
|
||||||
${whereSql}
|
|
||||||
ORDER BY cl.created_at DESC
|
|
||||||
LIMIT $${params.length + 1}
|
|
||||||
OFFSET $${params.length + 2}
|
|
||||||
`,
|
|
||||||
[...params, limit, offset],
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
items: rows.map((row) => ({
|
|
||||||
id: String(row.id),
|
|
||||||
userId: row.user_id == null ? "" : Number(row.user_id),
|
|
||||||
username: row.username || "",
|
|
||||||
model: row.model || "",
|
|
||||||
taskType: row.task_type,
|
|
||||||
resolution: row.resolution || null,
|
|
||||||
durationSeconds: row.duration_seconds == null ? null : Number(row.duration_seconds),
|
|
||||||
amountCents: Number(row.amount_cents || 0),
|
|
||||||
status: row.status,
|
|
||||||
createdAt: row.created_at,
|
|
||||||
})),
|
|
||||||
total: Number(countRow?.total || 0),
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[enterprise/usage/records] failed", error);
|
|
||||||
res.status(500).json({ error: "企业用量记录加载失败" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post("/enterprise/recharge", requireAuth, requireEnterpriseAdmin, async (req, res) => {
|
|
||||||
const { amountCents, paymentMethod = "wechat" } = req.body;
|
|
||||||
if (!amountCents || amountCents <= 0)
|
|
||||||
return res.status(400).json({ error: "充值金额必须大于0" });
|
|
||||||
|
|
||||||
const orderNo = generateOrderNo();
|
|
||||||
const enterpriseName = await getEnterpriseName(req.user.enterpriseId);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await pool.query(
|
|
||||||
`
|
|
||||||
INSERT INTO payment_orders (order_no, enterprise_id, enterprise_name, type, amount_cents, payment_method, status)
|
|
||||||
VALUES ($1, $2, $3, 'recharge', $4, $5, 'pending')
|
|
||||||
`,
|
|
||||||
[orderNo, req.user.enterpriseId, enterpriseName, Number(amountCents), paymentMethod],
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({ orderNo, amountCents, paymentMethod });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[enterprise/recharge] failed", error);
|
|
||||||
res.status(500).json({ error: "创建充值订单失败" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post("/enterprise/distribute", requireAuth, requireEnterpriseAdmin, async (req, res) => {
|
|
||||||
const { userId, amountCents, distributions } = req.body;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (distributions && Array.isArray(distributions)) {
|
|
||||||
for (const d of distributions) {
|
|
||||||
if (!d.userId || !d.amountCents || d.amountCents <= 0) {
|
|
||||||
return res
|
|
||||||
.status(400)
|
|
||||||
.json({ error: "每条分发记录必须包含有效的 userId 和 amountCents" });
|
|
||||||
}
|
|
||||||
await distributeCredits(req.user.enterpriseId, d.userId, d.amountCents, req.user.id);
|
|
||||||
}
|
|
||||||
res.json({ success: true, count: distributions.length });
|
|
||||||
} else if (userId && amountCents) {
|
|
||||||
if (amountCents <= 0) return res.status(400).json({ error: "分发积分必须大于0" });
|
|
||||||
const result = await distributeCredits(
|
|
||||||
req.user.enterpriseId,
|
|
||||||
userId,
|
|
||||||
amountCents,
|
|
||||||
req.user.id,
|
|
||||||
);
|
|
||||||
res.json({ success: true, ...result });
|
|
||||||
} else {
|
|
||||||
return res.status(400).json({ error: "缺少分发参数" });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[enterprise/distribute] failed", error);
|
|
||||||
res.status(400).json({ error: "分发参数处理失败" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get(
|
|
||||||
"/enterprise/employee-consumption",
|
|
||||||
requireAuth,
|
|
||||||
requireEnterpriseAdmin,
|
|
||||||
async (req, res) => {
|
|
||||||
const { period = "30d" } = req.query;
|
|
||||||
const periodStart = getPeriodStart(period);
|
|
||||||
const whereClause = periodStart ? `AND acl.created_at >= ${periodStart}` : "";
|
|
||||||
|
|
||||||
const { rows } = await pool.query(
|
|
||||||
`
|
|
||||||
SELECT
|
|
||||||
u.id AS user_id,
|
|
||||||
u.username,
|
|
||||||
u.balance_cents AS current_balance_cents,
|
|
||||||
COUNT(acl.id) AS total_calls,
|
|
||||||
COALESCE(SUM(CASE WHEN acl.cost_estimate IS NOT NULL THEN CAST(ROUND((acl.cost_estimate * 100)::numeric) AS INTEGER) ELSE 0 END), 0) AS total_cost_cents,
|
|
||||||
MAX(acl.created_at) AS last_active
|
|
||||||
FROM users u
|
|
||||||
LEFT JOIN api_call_logs acl ON acl.user_id = u.id AND acl.status = 'success'
|
|
||||||
WHERE u.enterprise_id = $1 AND u.enabled = 1 AND u.is_enterprise_admin = 0 ${whereClause}
|
|
||||||
GROUP BY u.id, u.username, u.balance_cents
|
|
||||||
ORDER BY total_cost_cents DESC
|
|
||||||
`,
|
|
||||||
[req.user.enterpriseId],
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json(
|
|
||||||
rows.map((r) => ({
|
|
||||||
userId: Number(r.user_id),
|
|
||||||
username: r.username,
|
|
||||||
currentBalanceCents: Number(r.current_balance_cents),
|
|
||||||
totalCalls: Number(r.total_calls),
|
|
||||||
totalCostCents: Number(r.total_cost_cents),
|
|
||||||
lastActive: r.last_active,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
router.post(
|
|
||||||
"/enterprise/purchase-package",
|
|
||||||
requireAuth,
|
|
||||||
requireEnterpriseAdmin,
|
|
||||||
async (req, res) => {
|
|
||||||
const { packageId, paymentMethod = "wechat" } = req.body;
|
|
||||||
if (!packageId) return res.status(400).json({ error: "缺少套餐ID" });
|
|
||||||
|
|
||||||
const {
|
|
||||||
rows: [pkg],
|
|
||||||
} = await pool.query("SELECT * FROM packages WHERE id = $1 AND enabled = 1", [packageId]);
|
|
||||||
if (!pkg) return res.status(404).json({ error: "套餐不存在或已下架" });
|
|
||||||
|
|
||||||
const orderNo = generateOrderNo();
|
|
||||||
const enterpriseName = await getEnterpriseName(req.user.enterpriseId);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await pool.query(
|
|
||||||
`
|
|
||||||
INSERT INTO payment_orders (order_no, enterprise_id, enterprise_name, type, amount_cents, package_id, payment_method, status)
|
|
||||||
VALUES ($1, $2, $3, 'package', $4, $5, $6, 'pending')
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
orderNo,
|
|
||||||
req.user.enterpriseId,
|
|
||||||
enterpriseName,
|
|
||||||
pkg.price_cents,
|
|
||||||
packageId,
|
|
||||||
paymentMethod,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({ orderNo, amountCents: pkg.price_cents, packageId, paymentMethod });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[enterprise/purchase-package] failed", error);
|
|
||||||
res.status(500).json({ error: "创建套餐订单失败" });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// ── Enterprise: Invoices ──────────────────────────────────────────────
|
|
||||||
|
|
||||||
router.post(
|
|
||||||
"/enterprise/invoice-apply",
|
|
||||||
requireAuth,
|
|
||||||
requireEnterpriseAdmin,
|
|
||||||
async (req, res) => {
|
|
||||||
const { paymentOrderId, type = "general", title, taxNo } = req.body;
|
|
||||||
if (!title) return res.status(400).json({ error: "缺少发票抬头" });
|
|
||||||
|
|
||||||
let amountCents = 0;
|
|
||||||
if (paymentOrderId) {
|
|
||||||
const {
|
|
||||||
rows: [order],
|
|
||||||
} = await pool.query(
|
|
||||||
"SELECT * FROM payment_orders WHERE id = $1 AND enterprise_id = $2 AND status = $3",
|
|
||||||
[paymentOrderId, req.user.enterpriseId, "paid"],
|
|
||||||
);
|
|
||||||
if (!order) return res.status(404).json({ error: "支付订单不存在或未支付" });
|
|
||||||
amountCents = order.amount_cents;
|
|
||||||
}
|
|
||||||
|
|
||||||
const enterpriseName = await getEnterpriseName(req.user.enterpriseId);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const {
|
|
||||||
rows: [row],
|
|
||||||
} = await pool.query(
|
|
||||||
`
|
|
||||||
INSERT INTO invoices (enterprise_id, enterprise_name, payment_order_id, type, title, tax_no, amount_cents, status)
|
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, 'pending')
|
|
||||||
RETURNING id
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
req.user.enterpriseId,
|
|
||||||
enterpriseName,
|
|
||||||
paymentOrderId || null,
|
|
||||||
type,
|
|
||||||
title,
|
|
||||||
taxNo || null,
|
|
||||||
amountCents,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({ id: row.id, success: true });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[enterprise/invoice-apply] failed", error);
|
|
||||||
res.status(500).json({ error: "申请发票失败" });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
router.get("/enterprise/invoices", requireAuth, requireEnterpriseAdmin, async (req, res) => {
|
|
||||||
const { rows } = await pool.query(
|
|
||||||
"SELECT * FROM invoices WHERE enterprise_id = $1 ORDER BY id DESC",
|
|
||||||
[req.user.enterpriseId],
|
|
||||||
);
|
|
||||||
res.json(
|
|
||||||
rows.map((row) => ({
|
|
||||||
id: Number(row.id),
|
|
||||||
type: row.type,
|
|
||||||
title: row.title,
|
|
||||||
taxNo: row.tax_no,
|
|
||||||
amountCents: row.amount_cents,
|
|
||||||
status: row.status,
|
|
||||||
invoiceNo: row.invoice_no,
|
|
||||||
invoiceUrl: row.invoice_url,
|
|
||||||
issuedAt: row.issued_at,
|
|
||||||
createdAt: row.created_at,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
registerEnterpriseRoutes,
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user