Przeglądaj źródła

feat: 用户信息认证

max 7 miesięcy temu
rodzic
commit
6de8321dd5

+ 8 - 0
README.md

@@ -189,3 +189,11 @@ export class DemoAppGoodsController extends BaseController {
 ### 低价服务器
 
 [阿里云、腾讯云、华为云低价云服务器,不限新老](https://cool-js.com/service/cloud)
+
+
+
+接口文档
+
+https://docs.sunpay.pro/#/zh/CARD/createorder
+
+https://docs-merchant.sunpay.pro/home

+ 10 - 0
src/comm/utils.ts

@@ -146,6 +146,16 @@ export class Utils {
     return dates;
   }
 
+  /**
+   * 获取当前时间
+   * @param newTime
+   */
+  getNewTime(format = 'YYYY-MM-DD HH:mm:ss') {
+    moment.locale('zh-cn');
+    const _newTime = moment().format(format);
+    return _newTime;
+  }
+
   /**
    * 字段转驼峰法
    * @param obj

+ 7 - 7
src/config/config.max.ts

@@ -9,14 +9,14 @@ export default {
     dataSource: {
       default: {
         type: 'mysql',
-        host: '192.168.2.101',
-        port: 6806,
+        // host: '192.168.2.101',
+        // port: 6806,
+        // username: 'root',
+        // password: 'admin',
+        host: '127.0.0.1',
+        port: 3306,
         username: 'root',
-        password: 'admin',
-        // host: '127.0.0.1',
-        // port: 3306,
-        // username: 'va',
-        // password: 'FHZ36dP527GacTty',
+        password: '12345678',
         database: 'va',
         // 自动建表 注意:线上部署的时候不要使用,有可能导致数据丢失
         synchronize: true,

+ 4 - 0
src/modules/api/middleware/authority.ts

@@ -88,6 +88,10 @@ export class BaseAuthorityMiddleware
           };
           return;
         }
+        /*如果说商户没有认证,我们需要提示商户进行认证*/
+        ctx.admin.merchant = merchantInfo
+        ctx.admin.openApi = true
+        await next();
       }
 
       await next();

+ 16 - 5
src/modules/api/service/customer.ts

@@ -30,24 +30,35 @@ export class CustomerService extends BaseService {
     // }
     if (isIndividual) {
       this.individualService.setIsOpenApi(true)
-      return await this.individualService.add(params);
+      return await this.individualService.add({
+        ...params,
+        ...params.individual,
+      });
     }
     this.businessService.setIsOpenApi(true)
-    return await this.businessService.add(params);
+    return await this.businessService.add({
+      ...params,
+      ...params.company,
+    });
     // TODO 过滤sunpay返回
   }
   async updateCustomer(params) {
     const isIndividual = params.customer_type === 'INDIVIDUAL';
-    // const isCompany = params.customer_type === 'COMPANY';
     if (!params?.individual && !params?.company) {
       throw new CoolCommException('company或individual必须传一个');
     }
     if (isIndividual) {
       this.individualService.setIsOpenApi(true)
-      return await this.individualService.update(params);
+      return await this.individualService.update({
+        ...params,
+        ...params.individual,
+      });
     }
     this.businessService.setIsOpenApi(true)
-    return await this.businessService.update(params);
+    return await this.businessService.update({
+      ...params,
+      ...params.company,
+    });
     // TODO 过滤sunpay返回
   }
   async addIndividual(params) {

+ 26 - 14
src/modules/base/service/sys/user.ts

@@ -12,6 +12,7 @@ import { BaseSysDepartmentEntity } from '../../entity/sys/department';
 import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
 import { IndividualEntity } from './../../../payment/entity/individual';
 import { BusinessEntity } from './../../../payment/entity/business';
+import {CustomerEntity} from "../../../payment/entity/customer";
 
 /**
  * 系统用户
@@ -36,6 +37,9 @@ export class BaseSysUserService extends BaseService {
   @InjectEntityModel(BusinessEntity)
   businessEntity: Repository<BusinessEntity>;
 
+  @InjectEntityModel(CustomerEntity)
+  customerEntity: Repository<CustomerEntity>;
+
   @InjectClient(CachingFactory, 'default')
   midwayCache: MidwayCache;
 
@@ -121,19 +125,27 @@ export class BaseSysUserService extends BaseService {
     });
 
     // 如果需要获取关联信息,可以分别查询
-    // let individualInfo = null;
-    // let businessInfo = null;
+    let individualInfo = null;
+    let businessInfo = null;
 
-    // if (merchant) {
-    //   // 分别查询个人或企业信息
-    //   individualInfo = await this.individualEntity.findOneBy({
-    //     merchantId: merchant.mchId
-    //   });
-    //
-    //   businessInfo = await this.businessEntity.findOneBy({
-    //     merchantId: merchant.mchId
-    //   });
-    // }
+    if (merchant) {
+      const customer = await this.customerEntity.findOne({
+        where: {
+          out_user_id: null,
+          merchantId: merchant.mchId
+        }
+      })
+      if(customer.type === 'COMPANY') {
+        businessInfo = await this.businessEntity.findOneBy({
+          customer_id: customer.customer_id,
+        });
+      } else {
+        // 分别查询个人或企业信息
+        individualInfo = await this.individualEntity.findOneBy({
+          customer_id: customer.customer_id,
+        });
+      }
+    }
 
     delete info?.password;
     return {
@@ -141,8 +153,8 @@ export class BaseSysUserService extends BaseService {
       merchant: merchant
         ? {
             ...merchant,
-            // individual: individualInfo,
-            // business: businessInfo,
+            individual: individualInfo,
+            business: businessInfo,
           }
         : null,
     };

+ 4 - 3
src/modules/payment/adapter/sunpay.adapter.ts

@@ -332,20 +332,20 @@ export class SunPayAdapter implements ChannelAdapter {
    * @returns
    */
   async setCustomerInfo(params: any = {}) {
+    // 兼容 白标 和 va平台的身份认证, 根据 openApi 判断
     // 保存客户信息
     const customer = await this.customerEntity.findOne({
       where: {
-        merchantId: params.out_user_id, // 商户ID
+        // 通过判断是否为开放api, 确认白标渠道
+        [params.openApi ? 'out_user_id' : 'merchantId']: params.out_user_id, // 商户ID
         channel: 'SUNPAY',
       },
     });
-    console.log(340, customer);
     let res = null;
     if (customer) {
       params.customer_id = customer.customer_id;
       res = await this.request('PUT', '/Fiat/Customer', {
         ...params,
-
       });
     } else {
       res = await this.request('POST', '/Fiat/Customer', {
@@ -361,6 +361,7 @@ export class SunPayAdapter implements ChannelAdapter {
       status: res.data.status,
       webhook_url: params.webhook_url,
       beneficiary_id: res.data.beneficiary_id,
+      out_user_id: params.openApi ? params.out_user_id : null // 白标用户是有 out_user_id , va商户没有
     });
 
     return res;

+ 1 - 1
src/modules/payment/controller/admin/wallet.ts

@@ -19,7 +19,7 @@ import { Context } from 'vm';
     where: async (ctx: Context) => {
       const { merchant, roleId } = ctx.admin;
       if ([1, 3].includes(roleId)) {
-        return [['merchantId=:merchantId', { merchantId: merchant.id }]];
+        return [['merchantId=:merchantId', { merchantId: merchant.mchId }]];
       }
       return [];
     },

+ 2 - 0
src/modules/payment/entity/customer.ts

@@ -14,6 +14,8 @@ export class CustomerEntity extends BaseEntity {
   type: string;
   @Column({ comment: '客户ID' })
   customer_id: string;
+  @Column({ comment: '白标商户ID', nullable: true })
+  out_user_id!: string;
   @Column({ comment: '状态' })
   status: string;
   @Column({ comment: 'webhook_url' })

+ 51 - 33
src/modules/payment/service/business.ts

@@ -5,6 +5,8 @@ import { InjectEntityModel } from '@midwayjs/typeorm';
 import { Repository } from 'typeorm';
 import { PaymentService } from './payment';
 import {MerchantEntity} from "../entity/merchant";
+import {CustomerEntity} from "../entity/customer";
+import {Utils} from "../../../comm/utils";
 @Provide()
 export class BusinessService extends BaseService {
   @InjectEntityModel(BusinessEntity)
@@ -13,12 +15,17 @@ export class BusinessService extends BaseService {
   @InjectEntityModel(MerchantEntity)
   merchantEntity: Repository<MerchantEntity>;
 
+  @InjectEntityModel(CustomerEntity)
+  customerEntity: Repository<CustomerEntity>;
+
   @Inject()
   paymentService: PaymentService;
   @Inject()
   ctx;
   @Config(ALL)
   config;
+  @Inject()
+  utils: Utils;
   @Init()
   async init() {
     await super.init();
@@ -29,32 +36,38 @@ export class BusinessService extends BaseService {
 
   /**
    * 添加或者更新当前商户的 business 客户信息
-   * @param param
+   * @param params
    * @returns
    */
-  async add(param) {
-    let merchantId = await this.getMerchantId(param);
+  async add(params) {
+    const { merchant } = this.ctx.admin;
+    const merchantId = merchant.mchId;
+    if (!params?.registration_date) {
+      params.registration_date = this.utils.getNewTime()
+    }
     const custom = {
-      ...param,
+      ...params,
       out_user_id: merchantId,
-      webhook_url: this.getWebhook_url(param),
+      webhook_url: this.getWebhook_url(params),
       customer_type: 'COMPANY',
+      openApi: this.ctx.admin?.openApi || false
     };
     let res = await this.paymentService
       .setChannel('SUNPAY')
       .setCustomerInfo(custom);
     if (merchantId) {
-      param.merchantId = param?.mch_id || param?.mchId || merchantId
+      params.merchantId = params?.mch_id || params?.mchId || merchantId
     }
     await super.add({
-      ...param,
-      ...param.company,
+      ...params,
+      ...params.company,
       customer_id: res.data.customer_id,
     });
     return res;
   }
   async update(param) {
-    let merchantId = this.getMerchantId(param);
+    const { merchant } = this.ctx.admin;
+    const merchantId = merchant.mchId;
     const custom = {
       ...param,
       out_user_id: merchantId,
@@ -64,10 +77,7 @@ export class BusinessService extends BaseService {
     const res = await this.paymentService
       .setChannel('SUNPAY')
       .setCustomerInfo(custom);
-    await super.update({
-      ...param,
-      ...param.company,
-    });
+    await super.update(param);
     return res;
   }
 
@@ -75,23 +85,6 @@ export class BusinessService extends BaseService {
     this._isOpenApi = payload
   }
 
-  async getMerchantId(params) {
-    let merchantId;
-    if (this._isOpenApi) {
-      merchantId = params.out_user_id;
-    } else if (params?.mch_id || params?.mchId) {
-      // const  merchantInfo = await this.merchantEntity.findOneBy({
-      //   mchId: params?.mch_id || params?.mchId
-      // })
-      merchantId = params?.mch_id || params?.mchId
-    } else {
-      const { merchant } = this.ctx.admin;
-      merchantId = merchant.mchId;
-    }
-
-    return merchantId
-  }
-
   getWebhook_url(params) {
     if (this._isOpenApi) {
       return params.webhook_url
@@ -99,10 +92,35 @@ export class BusinessService extends BaseService {
     return this.config.callback.sunpay
   }
 
-
   async page(params: any) {
-    const merchantId = await this.getMerchantId(params);
-    const query = `SELECT * FROM business WHERE merchantId = '${merchantId}' ORDER BY merchantId ASC`;
+    const { merchant, roleIds } = this.ctx.admin;
+    const merchantId = merchant.mchId;
+    // 初始化查询
+    let query = '';
+    const queryParams: any[] = [];
+    if (roleIds.filter(id => [1, 3].includes(id)).length) {
+      // 角色为 1 或 3 时,仅查询 business 表
+      query = `
+        SELECT *
+        FROM business
+        WHERE business.merchantId = '${merchantId}'
+        ORDER BY business.merchantId ASC
+      `;
+    } else {
+      // 其他角色,关联查询 business 和 customer 表
+      const out_user_idIsNull = this.ctx.admin?.openApi ? `AND customer.out_user_id = '${merchantId}'` : `AND customer.out_user_id is null`;
+      query = `
+      SELECT business.*
+      FROM business
+      JOIN customer ON business.customer_id = customer.customer_id
+      WHERE business.merchantId = '${merchantId}'
+        ${out_user_idIsNull}
+      ORDER BY business.merchantId ASC
+    `;
+    }
+    // 打印调试信息
+    console.log('SQL Query:', query, 'Params:', queryParams, 'RoleIds:', roleIds);
+    // 执行分页查询
     return this.sqlRenderPage(query, params, false);
   }
 }

+ 48 - 10
src/modules/payment/service/individual.ts

@@ -7,6 +7,7 @@ import { InjectEntityModel } from '@midwayjs/typeorm';
 import { Repository } from 'typeorm';
 import { PaymentService } from './payment';
 import {MerchantEntity} from "../entity/merchant";
+import {Utils} from "../../../comm/utils";
 @Provide()
 export class IndividualService extends BaseService {
   @InjectEntityModel(IndividualEntity)
@@ -23,6 +24,10 @@ export class IndividualService extends BaseService {
   merchantEntity: Repository<MerchantEntity>;
   @Config(ALL)
   config;
+
+  @Inject()
+  utils: Utils;
+
   @Init()
   async init() {
     await super.init();
@@ -35,27 +40,34 @@ export class IndividualService extends BaseService {
    * @param param
    * @returns
    */
-  async add(param) {
-    let merchantId = await this.getMerchantId(param);
+  async add(params) {
+    const { merchant } = this.ctx.admin;
+    const merchantId = merchant.mchId;
+    if (!params?.registration_date) {
+      params.registration_date = this.utils.getNewTime()
+    }
     const custom = {
-      ...param,
+      ...params,
       out_user_id: merchantId,
-      webhook_url: this.getWebhook_url(param),
+      webhook_url: this.getWebhook_url(params),
       customer_type: 'INDIVIDUAL',
+      openApi: this.ctx.admin?.openApi || false
     };
     let res = await this.paymentService
       .setChannel('SUNPAY')
       .setCustomerInfo(custom);
-    param.merchantId = merchantId
+    if (merchantId) {
+      params.merchantId = params?.mch_id || params?.mchId || merchantId
+    }
     await super.add({
-      ...param,
-      ...param.company,
+      ...params,
       customer_id: res.data.customer_id,
     });
     return res;
   }
   async update(param) {
-    let merchantId = this.getMerchantId(param);
+    const { merchant } = this.ctx.admin;
+    const merchantId = merchant.mchId;
     const custom = {
       ...param,
       out_user_id: merchantId,
@@ -73,8 +85,34 @@ export class IndividualService extends BaseService {
   }
 
   async page(params: any) {
-    const merchantId = await this.getMerchantId(params);
-    const query = `SELECT * FROM individual WHERE merchantId = '${merchantId}' ORDER BY merchantId ASC`;
+    const { merchant, roleIds } = this.ctx.admin;
+    const merchantId = merchant.mchId;
+    // 初始化查询
+    let query = '';
+    const queryParams: any[] = [];
+    if (roleIds.filter(id => [1, 3].includes(id)).length) {
+      // 角色为 1 或 3 时,仅查询 individual 表
+      query = `
+        SELECT *
+        FROM individual
+        WHERE individual.merchantId = '${merchantId}'
+        ORDER BY individual.merchantId ASC
+      `;
+    } else {
+      // 其他角色,关联查询 individual 和 customer 表
+      const out_user_idIsNull = this.ctx.admin?.openApi ? `AND customer.out_user_id = '${merchantId}'` : `AND customer.out_user_id is null`;
+      query = `
+        SELECT individual.*
+        FROM individual
+               JOIN customer ON individual.customer_id = customer.customer_id
+        WHERE individual.merchantId = '${merchantId}'
+          ${out_user_idIsNull}
+        ORDER BY individual.merchantId ASC
+      `;
+    }
+    // 打印调试信息
+    console.log('SQL Query:', query, 'Params:', queryParams, 'RoleIds:', roleIds);
+    // 执行分页查询
     return this.sqlRenderPage(query, params, false);
   }
 

+ 1 - 0
src/modules/payment/service/payment.ts

@@ -50,6 +50,7 @@ export class PaymentService extends BaseService {
       out_user_id: params.out_user_id,
       webhook_url: params.webhook_url,
       customer_type: params.customer_type,
+      openApi: params?.openApi || false,
     };
     delete params.merchantId;
     delete params.webhook_url;

+ 14 - 1
src/modules/payment/service/wallet.ts

@@ -28,8 +28,21 @@ export class WalletService extends BaseService {
    * 描述
    */
   async add(param: any) {
+
+    // 商户给自己创建钱包
+    // 商户给自己的用户创建钱包
+    // 商户给自己创建钱包
+
+    if(!param.customer_id) {
+      // 查找商户的 customer_id
+      // api访问
+      // 网页中访问
+      const { merchant } = this.ctx.admin;
+      console.log(41, merchant)
+    }
+
     const customer = await this.customerEntity.findOneBy({
-      customer_id: param.customerId
+      customer_id: param.customer_id
     });
     if (!customer) {
       throw new CoolCommException('客户不存在,请先创建认证');