333 lines
10 KiB
TypeScript
333 lines
10 KiB
TypeScript
import type { WecomPluginPermission } from '@dukang/shared-types';
|
|
import { allowedWecomPluginOpenApiPaths } from '@dukang/shared-types';
|
|
|
|
const envelope = (dataSchema: Record<string, unknown>) => ({
|
|
type: 'object',
|
|
properties: {
|
|
code: { type: 'integer', example: 0 },
|
|
message: { type: 'string', example: 'ok' },
|
|
data: dataSchema,
|
|
},
|
|
required: ['code', 'message', 'data'],
|
|
});
|
|
|
|
const qParam = {
|
|
name: 'q',
|
|
in: 'query',
|
|
required: true,
|
|
schema: { type: 'string' },
|
|
description: '查询关键词',
|
|
};
|
|
|
|
const pageParams = [
|
|
{
|
|
name: 'page',
|
|
in: 'query',
|
|
required: false,
|
|
schema: { type: 'integer', default: 1, minimum: 1 },
|
|
},
|
|
{
|
|
name: 'pageSize',
|
|
in: 'query',
|
|
required: false,
|
|
schema: { type: 'integer', default: 5, minimum: 1, maximum: 10 },
|
|
description: '默认 5,最大 10',
|
|
},
|
|
];
|
|
|
|
const dateRangeParams = [
|
|
{
|
|
name: 'from',
|
|
in: 'query',
|
|
required: false,
|
|
schema: { type: 'string' },
|
|
description: '起始日期 YYYY-MM-DD 或 ISO',
|
|
},
|
|
{
|
|
name: 'to',
|
|
in: 'query',
|
|
required: false,
|
|
schema: { type: 'string' },
|
|
description: '结束日期 YYYY-MM-DD 或 ISO(含当天)',
|
|
},
|
|
];
|
|
|
|
const unauthorized = {
|
|
description: '缺少或错误的 X-Api-Key,或插件未启用',
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
code: { type: 'integer' },
|
|
message: { type: 'string' },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
function listPath(summary: string, description: string, qDescription: string) {
|
|
return {
|
|
get: {
|
|
summary,
|
|
description,
|
|
operationId: summary,
|
|
parameters: [{ ...qParam, description: qDescription }, ...pageParams],
|
|
responses: {
|
|
200: {
|
|
description: '查询结果',
|
|
content: {
|
|
'application/json': {
|
|
schema: envelope({
|
|
type: 'object',
|
|
properties: {
|
|
total: { type: 'integer' },
|
|
items: { type: 'array', items: { type: 'object' } },
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
/** 按实例权限过滤 paths,供企微第 2 步对照配置 */
|
|
export function filterWecomPluginOpenApi(
|
|
spec: typeof WECOM_PLUGIN_OPENAPI,
|
|
permissions: WecomPluginPermission[],
|
|
title?: string,
|
|
) {
|
|
const allowedPaths = new Set(allowedWecomPluginOpenApiPaths(permissions));
|
|
const paths: Record<string, unknown> = {};
|
|
for (const [path, def] of Object.entries(spec.paths)) {
|
|
if (allowedPaths.has(path)) paths[path] = def;
|
|
}
|
|
return {
|
|
...spec,
|
|
info: {
|
|
...spec.info,
|
|
title: title || spec.info.title,
|
|
},
|
|
paths,
|
|
};
|
|
}
|
|
|
|
/** OpenAPI 3.0:企微「添加插件工具」可导入。须原样返回,不要套 {code,message,data}。 */
|
|
export const WECOM_PLUGIN_OPENAPI = {
|
|
openapi: '3.0.3',
|
|
info: {
|
|
title: '杜康好客运营查询',
|
|
description:
|
|
'企业内部只读查询。手机号已脱敏。鉴权:Header X-Api-Key。响应除本文件外均为 { code, message, data }。',
|
|
version: '1.0.0',
|
|
},
|
|
servers: [
|
|
{ url: 'https://api.dukanghaoke.com/api/v1/wecom/plugin', description: '生产' },
|
|
{ url: 'https://api-test.dukanghaoke.com/api/v1/wecom/plugin', description: '测试' },
|
|
],
|
|
security: [{ ApiKeyAuth: [] }],
|
|
components: {
|
|
securitySchemes: {
|
|
ApiKeyAuth: {
|
|
type: 'apiKey',
|
|
in: 'header',
|
|
name: 'X-Api-Key',
|
|
},
|
|
},
|
|
},
|
|
paths: {
|
|
'/orders': listPath('查询订单', '按订单号模糊查询', '订单号,如 DK20260903xxxx'),
|
|
'/users': listPath('查询用户', '按用户号或 11 位手机号查询;手机号脱敏', '用户号或手机号'),
|
|
'/stores': listPath(
|
|
'查询门店',
|
|
'按门店名称模糊查询,返回经营数据:评分、核销笔数、累计核销好客权益、合伙人、审核状态',
|
|
'门店名称关键词',
|
|
),
|
|
'/store-audits': {
|
|
get: {
|
|
summary: '门店入驻审核列表',
|
|
description: '默认 status=PENDING;可按门店名筛选',
|
|
operationId: '查询门店入驻审核',
|
|
parameters: [
|
|
{ name: 'q', in: 'query', required: false, schema: { type: 'string' }, description: '门店名称' },
|
|
{
|
|
name: 'status',
|
|
in: 'query',
|
|
required: false,
|
|
schema: { type: 'string', enum: ['PENDING', 'APPROVED', 'REJECTED'], default: 'PENDING' },
|
|
},
|
|
...pageParams,
|
|
],
|
|
responses: {
|
|
200: { description: '审核列表', content: { 'application/json': { schema: envelope({ type: 'object' }) } } },
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
},
|
|
'/store-info-audits': {
|
|
get: {
|
|
summary: '门店信息变更审核列表',
|
|
operationId: '查询门店信息变更审核',
|
|
parameters: [
|
|
{
|
|
name: 'status',
|
|
in: 'query',
|
|
required: false,
|
|
schema: { type: 'string', enum: ['PENDING', 'APPROVED', 'REJECTED'], default: 'PENDING' },
|
|
},
|
|
...pageParams,
|
|
],
|
|
responses: {
|
|
200: { description: '列表', content: { 'application/json': { schema: envelope({ type: 'object' }) } } },
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
},
|
|
'/store-info-audits/{id}': {
|
|
get: {
|
|
summary: '门店信息变更审核对比',
|
|
description: '返回变更字段 live vs proposed 对照',
|
|
operationId: '查询门店信息变更详情',
|
|
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],
|
|
responses: {
|
|
200: { description: '详情含 diffs', content: { 'application/json': { schema: envelope({ type: 'object' }) } } },
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
},
|
|
'/store-package-audits': {
|
|
get: {
|
|
summary: '门店套餐审核列表',
|
|
operationId: '查询门店套餐审核',
|
|
parameters: [
|
|
{
|
|
name: 'status',
|
|
in: 'query',
|
|
required: false,
|
|
schema: { type: 'string', enum: ['PENDING', 'APPROVED', 'REJECTED'], default: 'PENDING' },
|
|
},
|
|
...pageParams,
|
|
],
|
|
responses: {
|
|
200: { description: '列表', content: { 'application/json': { schema: envelope({ type: 'object' }) } } },
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
},
|
|
'/store-package-audits/{id}': {
|
|
get: {
|
|
summary: '门店套餐审核对比',
|
|
description: '返回 proposedPackages 与 livePackages 对照',
|
|
operationId: '查询门店套餐审核详情',
|
|
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],
|
|
responses: {
|
|
200: { description: '详情含套餐对比', content: { 'application/json': { schema: envelope({ type: 'object' }) } } },
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
},
|
|
'/partners': listPath('查询合伙人', '按姓名、公司名、手机号或 ID 搜索主账号', '合伙人关键词'),
|
|
'/partners/{partnerId}/users': {
|
|
get: {
|
|
summary: '合伙人关联用户',
|
|
operationId: '查询合伙人关联用户',
|
|
parameters: [
|
|
{ name: 'partnerId', in: 'path', required: true, schema: { type: 'string' } },
|
|
...dateRangeParams,
|
|
...pageParams,
|
|
],
|
|
responses: {
|
|
200: { description: '关联用户列表', content: { 'application/json': { schema: envelope({ type: 'object' }) } } },
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
},
|
|
'/partners/{partnerId}/stores': {
|
|
get: {
|
|
summary: '合伙人名下门店',
|
|
operationId: '查询合伙人门店',
|
|
parameters: [
|
|
{ name: 'partnerId', in: 'path', required: true, schema: { type: 'string' } },
|
|
...dateRangeParams,
|
|
...pageParams,
|
|
],
|
|
responses: {
|
|
200: { description: '门店及经营数据', content: { 'application/json': { schema: envelope({ type: 'object' }) } } },
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
},
|
|
'/partners/{partnerId}/orders': {
|
|
get: {
|
|
summary: '合伙人相关订单',
|
|
description: '佣金归属 partnerAccountIdAtPay 的订单',
|
|
operationId: '查询合伙人订单',
|
|
parameters: [
|
|
{ name: 'partnerId', in: 'path', required: true, schema: { type: 'string' } },
|
|
...dateRangeParams,
|
|
...pageParams,
|
|
],
|
|
responses: {
|
|
200: { description: '订单列表', content: { 'application/json': { schema: envelope({ type: 'object' }) } } },
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
},
|
|
'/redeems': listPath('查询核销', '按核销单号或门店名查询', '核销单号或门店名'),
|
|
'/promo-codes': listPath('查询推广码', '按推广码 code 或名称查询', '推广码或名称'),
|
|
'/promo-codes/{code}/stats': {
|
|
get: {
|
|
summary: '推广码统计',
|
|
operationId: '查询推广码统计',
|
|
parameters: [
|
|
{
|
|
name: 'code',
|
|
in: 'path',
|
|
required: true,
|
|
schema: { type: 'string' },
|
|
description: '推广码 code',
|
|
},
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: '扫码/成交统计',
|
|
content: { 'application/json': { schema: envelope({ type: 'object' }) } },
|
|
},
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
},
|
|
'/metrics': {
|
|
get: {
|
|
summary: '经营指标',
|
|
description:
|
|
'today=今日截至当前;daily/weekly/monthly 与企微经营报告同一口径。stats 含 users/partners/stores/orders 存量与增量;storesIncrement 与 newStores 均为新增门店数。',
|
|
operationId: '查询经营指标',
|
|
parameters: [
|
|
{
|
|
name: 'kind',
|
|
in: 'query',
|
|
required: false,
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['today', 'daily', 'weekly', 'monthly'],
|
|
default: 'today',
|
|
},
|
|
},
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: '存量与新增',
|
|
content: { 'application/json': { schema: envelope({ type: 'object' }) } },
|
|
},
|
|
401: unauthorized,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} as const;
|