feat: add kyc api

This commit is contained in:
王性驊 2025-04-08 17:20:34 +08:00
parent a2729b95ca
commit b5a12ceaa2
17 changed files with 1902 additions and 128 deletions

View File

@ -14,20 +14,28 @@ import (
)
type (
Category = product.Category
CategoryReq = product.CategoryReq
CreateCategoryReq = product.CreateCategoryReq
CreateTagsReq = product.CreateTagsReq
ListCategoryReq = product.ListCategoryReq
ListCategoryResp = product.ListCategoryResp
ListTagsReq = product.ListTagsReq
ListTagsResp = product.ListTagsResp
ModifyCategoryReq = product.ModifyCategoryReq
ModifyTagsReq = product.ModifyTagsReq
NoneReq = product.NoneReq
OKResp = product.OKResp
Tags = product.Tags
TagsReq = product.TagsReq
Category = product.Category
CategoryReq = product.CategoryReq
CreateCategoryReq = product.CreateCategoryReq
CreateKYCReq = product.CreateKYCReq
CreateTagsReq = product.CreateTagsReq
FindKYCByIDReq = product.FindKYCByIDReq
FindLatestKYCByUIDReq = product.FindLatestKYCByUIDReq
KYC = product.KYC
ListCategoryReq = product.ListCategoryReq
ListCategoryResp = product.ListCategoryResp
ListKYCReq = product.ListKYCReq
ListKYCResp = product.ListKYCResp
ListTagsReq = product.ListTagsReq
ListTagsResp = product.ListTagsResp
ModifyCategoryReq = product.ModifyCategoryReq
ModifyTagsReq = product.ModifyTagsReq
NoneReq = product.NoneReq
OKResp = product.OKResp
Tags = product.Tags
TagsReq = product.TagsReq
UpdateKYCInfoReq = product.UpdateKYCInfoReq
UpdateKYCStatusReq = product.UpdateKYCStatusReq
Product interface {
// ====================== Category Service Start ======================
@ -50,6 +58,18 @@ type (
GetTags(ctx context.Context, in *TagsReq, opts ...grpc.CallOption) (*Tags, error)
// ListTags 建立 tags
ListTags(ctx context.Context, in *ListTagsReq, opts ...grpc.CallOption) (*ListTagsResp, error)
// ====================== Tags Service End ======================
CreateKYC(ctx context.Context, in *CreateKYCReq, opts ...grpc.CallOption) (*OKResp, error)
// FindLatestKYCByUID 根據使用者 UID 查詢最新 KYC 紀錄
FindLatestKYCByUID(ctx context.Context, in *FindLatestKYCByUIDReq, opts ...grpc.CallOption) (*KYC, error)
// FindKYCByID 根據 KYC ID 查詢
FindKYCByID(ctx context.Context, in *FindKYCByIDReq, opts ...grpc.CallOption) (*KYC, error)
// ListKYC 分頁查詢 KYC 清單(後台審核用)
ListKYC(ctx context.Context, in *ListKYCReq, opts ...grpc.CallOption) (*ListKYCResp, error)
// UpdateKYCStatus 更新 KYC 審核狀態與原因
UpdateKYCStatus(ctx context.Context, in *UpdateKYCStatusReq, opts ...grpc.CallOption) (*OKResp, error)
// UpdateKYCInfo 更新使用者的 KYC尚未審核
UpdateKYCInfo(ctx context.Context, in *UpdateKYCInfoReq, opts ...grpc.CallOption) (*OKResp, error)
}
defaultProduct struct {
@ -122,3 +142,39 @@ func (m *defaultProduct) ListTags(ctx context.Context, in *ListTagsReq, opts ...
client := product.NewProductClient(m.cli.Conn())
return client.ListTags(ctx, in, opts...)
}
// ====================== Tags Service End ======================
func (m *defaultProduct) CreateKYC(ctx context.Context, in *CreateKYCReq, opts ...grpc.CallOption) (*OKResp, error) {
client := product.NewProductClient(m.cli.Conn())
return client.CreateKYC(ctx, in, opts...)
}
// FindLatestKYCByUID 根據使用者 UID 查詢最新 KYC 紀錄
func (m *defaultProduct) FindLatestKYCByUID(ctx context.Context, in *FindLatestKYCByUIDReq, opts ...grpc.CallOption) (*KYC, error) {
client := product.NewProductClient(m.cli.Conn())
return client.FindLatestKYCByUID(ctx, in, opts...)
}
// FindKYCByID 根據 KYC ID 查詢
func (m *defaultProduct) FindKYCByID(ctx context.Context, in *FindKYCByIDReq, opts ...grpc.CallOption) (*KYC, error) {
client := product.NewProductClient(m.cli.Conn())
return client.FindKYCByID(ctx, in, opts...)
}
// ListKYC 分頁查詢 KYC 清單(後台審核用)
func (m *defaultProduct) ListKYC(ctx context.Context, in *ListKYCReq, opts ...grpc.CallOption) (*ListKYCResp, error) {
client := product.NewProductClient(m.cli.Conn())
return client.ListKYC(ctx, in, opts...)
}
// UpdateKYCStatus 更新 KYC 審核狀態與原因
func (m *defaultProduct) UpdateKYCStatus(ctx context.Context, in *UpdateKYCStatusReq, opts ...grpc.CallOption) (*OKResp, error) {
client := product.NewProductClient(m.cli.Conn())
return client.UpdateKYCStatus(ctx, in, opts...)
}
// UpdateKYCInfo 更新使用者的 KYC尚未審核
func (m *defaultProduct) UpdateKYCInfo(ctx context.Context, in *UpdateKYCInfoReq, opts ...grpc.CallOption) (*OKResp, error) {
client := product.NewProductClient(m.cli.Conn())
return client.UpdateKYCInfo(ctx, in, opts...)
}

File diff suppressed because it is too large Load Diff

View File

@ -19,16 +19,22 @@ import (
const _ = grpc.SupportPackageIsVersion9
const (
Product_CreateCategory_FullMethodName = "/product.Product/CreateCategory"
Product_ModifyCategory_FullMethodName = "/product.Product/ModifyCategory"
Product_DeleteCategory_FullMethodName = "/product.Product/DeleteCategory"
Product_GetCategory_FullMethodName = "/product.Product/GetCategory"
Product_ListCategory_FullMethodName = "/product.Product/ListCategory"
Product_CreateTags_FullMethodName = "/product.Product/CreateTags"
Product_ModifyTags_FullMethodName = "/product.Product/ModifyTags"
Product_DeleteTags_FullMethodName = "/product.Product/DeleteTags"
Product_GetTags_FullMethodName = "/product.Product/GetTags"
Product_ListTags_FullMethodName = "/product.Product/ListTags"
Product_CreateCategory_FullMethodName = "/product.Product/CreateCategory"
Product_ModifyCategory_FullMethodName = "/product.Product/ModifyCategory"
Product_DeleteCategory_FullMethodName = "/product.Product/DeleteCategory"
Product_GetCategory_FullMethodName = "/product.Product/GetCategory"
Product_ListCategory_FullMethodName = "/product.Product/ListCategory"
Product_CreateTags_FullMethodName = "/product.Product/CreateTags"
Product_ModifyTags_FullMethodName = "/product.Product/ModifyTags"
Product_DeleteTags_FullMethodName = "/product.Product/DeleteTags"
Product_GetTags_FullMethodName = "/product.Product/GetTags"
Product_ListTags_FullMethodName = "/product.Product/ListTags"
Product_CreateKYC_FullMethodName = "/product.Product/CreateKYC"
Product_FindLatestKYCByUID_FullMethodName = "/product.Product/FindLatestKYCByUID"
Product_FindKYCByID_FullMethodName = "/product.Product/FindKYCByID"
Product_ListKYC_FullMethodName = "/product.Product/ListKYC"
Product_UpdateKYCStatus_FullMethodName = "/product.Product/UpdateKYCStatus"
Product_UpdateKYCInfo_FullMethodName = "/product.Product/UpdateKYCInfo"
)
// ProductClient is the client API for Product service.
@ -58,6 +64,20 @@ type ProductClient interface {
GetTags(ctx context.Context, in *TagsReq, opts ...grpc.CallOption) (*Tags, error)
// ListTags 建立 tags
ListTags(ctx context.Context, in *ListTagsReq, opts ...grpc.CallOption) (*ListTagsResp, error)
// ====================== Tags Service End ======================
// ====================== Know You Customer Service Start ======================
// CreateKYC 建立 KYC 資料
CreateKYC(ctx context.Context, in *CreateKYCReq, opts ...grpc.CallOption) (*OKResp, error)
// FindLatestKYCByUID 根據使用者 UID 查詢最新 KYC 紀錄
FindLatestKYCByUID(ctx context.Context, in *FindLatestKYCByUIDReq, opts ...grpc.CallOption) (*KYC, error)
// FindKYCByID 根據 KYC ID 查詢
FindKYCByID(ctx context.Context, in *FindKYCByIDReq, opts ...grpc.CallOption) (*KYC, error)
// ListKYC 分頁查詢 KYC 清單(後台審核用)
ListKYC(ctx context.Context, in *ListKYCReq, opts ...grpc.CallOption) (*ListKYCResp, error)
// UpdateKYCStatus 更新 KYC 審核狀態與原因
UpdateKYCStatus(ctx context.Context, in *UpdateKYCStatusReq, opts ...grpc.CallOption) (*OKResp, error)
// UpdateKYCInfo 更新使用者的 KYC尚未審核
UpdateKYCInfo(ctx context.Context, in *UpdateKYCInfoReq, opts ...grpc.CallOption) (*OKResp, error)
}
type productClient struct {
@ -168,6 +188,66 @@ func (c *productClient) ListTags(ctx context.Context, in *ListTagsReq, opts ...g
return out, nil
}
func (c *productClient) CreateKYC(ctx context.Context, in *CreateKYCReq, opts ...grpc.CallOption) (*OKResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(OKResp)
err := c.cc.Invoke(ctx, Product_CreateKYC_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productClient) FindLatestKYCByUID(ctx context.Context, in *FindLatestKYCByUIDReq, opts ...grpc.CallOption) (*KYC, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(KYC)
err := c.cc.Invoke(ctx, Product_FindLatestKYCByUID_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productClient) FindKYCByID(ctx context.Context, in *FindKYCByIDReq, opts ...grpc.CallOption) (*KYC, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(KYC)
err := c.cc.Invoke(ctx, Product_FindKYCByID_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productClient) ListKYC(ctx context.Context, in *ListKYCReq, opts ...grpc.CallOption) (*ListKYCResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListKYCResp)
err := c.cc.Invoke(ctx, Product_ListKYC_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productClient) UpdateKYCStatus(ctx context.Context, in *UpdateKYCStatusReq, opts ...grpc.CallOption) (*OKResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(OKResp)
err := c.cc.Invoke(ctx, Product_UpdateKYCStatus_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *productClient) UpdateKYCInfo(ctx context.Context, in *UpdateKYCInfoReq, opts ...grpc.CallOption) (*OKResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(OKResp)
err := c.cc.Invoke(ctx, Product_UpdateKYCInfo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ProductServer is the server API for Product service.
// All implementations must embed UnimplementedProductServer
// for forward compatibility.
@ -195,6 +275,20 @@ type ProductServer interface {
GetTags(context.Context, *TagsReq) (*Tags, error)
// ListTags 建立 tags
ListTags(context.Context, *ListTagsReq) (*ListTagsResp, error)
// ====================== Tags Service End ======================
// ====================== Know You Customer Service Start ======================
// CreateKYC 建立 KYC 資料
CreateKYC(context.Context, *CreateKYCReq) (*OKResp, error)
// FindLatestKYCByUID 根據使用者 UID 查詢最新 KYC 紀錄
FindLatestKYCByUID(context.Context, *FindLatestKYCByUIDReq) (*KYC, error)
// FindKYCByID 根據 KYC ID 查詢
FindKYCByID(context.Context, *FindKYCByIDReq) (*KYC, error)
// ListKYC 分頁查詢 KYC 清單(後台審核用)
ListKYC(context.Context, *ListKYCReq) (*ListKYCResp, error)
// UpdateKYCStatus 更新 KYC 審核狀態與原因
UpdateKYCStatus(context.Context, *UpdateKYCStatusReq) (*OKResp, error)
// UpdateKYCInfo 更新使用者的 KYC尚未審核
UpdateKYCInfo(context.Context, *UpdateKYCInfoReq) (*OKResp, error)
mustEmbedUnimplementedProductServer()
}
@ -235,6 +329,24 @@ func (UnimplementedProductServer) GetTags(context.Context, *TagsReq) (*Tags, err
func (UnimplementedProductServer) ListTags(context.Context, *ListTagsReq) (*ListTagsResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListTags not implemented")
}
func (UnimplementedProductServer) CreateKYC(context.Context, *CreateKYCReq) (*OKResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateKYC not implemented")
}
func (UnimplementedProductServer) FindLatestKYCByUID(context.Context, *FindLatestKYCByUIDReq) (*KYC, error) {
return nil, status.Errorf(codes.Unimplemented, "method FindLatestKYCByUID not implemented")
}
func (UnimplementedProductServer) FindKYCByID(context.Context, *FindKYCByIDReq) (*KYC, error) {
return nil, status.Errorf(codes.Unimplemented, "method FindKYCByID not implemented")
}
func (UnimplementedProductServer) ListKYC(context.Context, *ListKYCReq) (*ListKYCResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListKYC not implemented")
}
func (UnimplementedProductServer) UpdateKYCStatus(context.Context, *UpdateKYCStatusReq) (*OKResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateKYCStatus not implemented")
}
func (UnimplementedProductServer) UpdateKYCInfo(context.Context, *UpdateKYCInfoReq) (*OKResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateKYCInfo not implemented")
}
func (UnimplementedProductServer) mustEmbedUnimplementedProductServer() {}
func (UnimplementedProductServer) testEmbeddedByValue() {}
@ -436,6 +548,114 @@ func _Product_ListTags_Handler(srv interface{}, ctx context.Context, dec func(in
return interceptor(ctx, in, info, handler)
}
func _Product_CreateKYC_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateKYCReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductServer).CreateKYC(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Product_CreateKYC_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductServer).CreateKYC(ctx, req.(*CreateKYCReq))
}
return interceptor(ctx, in, info, handler)
}
func _Product_FindLatestKYCByUID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FindLatestKYCByUIDReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductServer).FindLatestKYCByUID(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Product_FindLatestKYCByUID_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductServer).FindLatestKYCByUID(ctx, req.(*FindLatestKYCByUIDReq))
}
return interceptor(ctx, in, info, handler)
}
func _Product_FindKYCByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FindKYCByIDReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductServer).FindKYCByID(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Product_FindKYCByID_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductServer).FindKYCByID(ctx, req.(*FindKYCByIDReq))
}
return interceptor(ctx, in, info, handler)
}
func _Product_ListKYC_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListKYCReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductServer).ListKYC(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Product_ListKYC_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductServer).ListKYC(ctx, req.(*ListKYCReq))
}
return interceptor(ctx, in, info, handler)
}
func _Product_UpdateKYCStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateKYCStatusReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductServer).UpdateKYCStatus(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Product_UpdateKYCStatus_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductServer).UpdateKYCStatus(ctx, req.(*UpdateKYCStatusReq))
}
return interceptor(ctx, in, info, handler)
}
func _Product_UpdateKYCInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateKYCInfoReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProductServer).UpdateKYCInfo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Product_UpdateKYCInfo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProductServer).UpdateKYCInfo(ctx, req.(*UpdateKYCInfoReq))
}
return interceptor(ctx, in, info, handler)
}
// Product_ServiceDesc is the grpc.ServiceDesc for Product service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -483,6 +703,30 @@ var Product_ServiceDesc = grpc.ServiceDesc{
MethodName: "ListTags",
Handler: _Product_ListTags_Handler,
},
{
MethodName: "CreateKYC",
Handler: _Product_CreateKYC_Handler,
},
{
MethodName: "FindLatestKYCByUID",
Handler: _Product_FindLatestKYCByUID_Handler,
},
{
MethodName: "FindKYCByID",
Handler: _Product_FindKYCByID_Handler,
},
{
MethodName: "ListKYC",
Handler: _Product_ListKYC_Handler,
},
{
MethodName: "UpdateKYCStatus",
Handler: _Product_UpdateKYCStatus_Handler,
},
{
MethodName: "UpdateKYCInfo",
Handler: _Product_UpdateKYCInfo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "generate/protobuf/product.proto",

View File

@ -86,6 +86,97 @@ message ListTagsResp {
repeated Tags data=3;
}
// ====================== KYC Param ======================
message KYC {
string id = 1; // MongoDB ObjectID
string uid = 2; // UID
string country_region = 3; // "TW", "JP", "US"...
string name = 4; //
string identification = 5; // or
string identification_type = 6; // ID ID_CARD, PASSPORT, RESIDENT_CERT
string address = 7; //
string postal_code = 8; // 使
string id_front_image = 9; // /
string id_back_image = 10; // /
string bank_statement_img = 11; //
string bank_code = 12; // SWIFT
string bank_name = 13; //
string branch_code = 14; //
string branch_name = 15; //
string bank_account = 16; //
string status = 17; // PENDING, APPROVED, REJECTED
string reject_reason = 18; //
int64 updated_at = 19; // timestamp
int64 created_at = 20; // timestamp
}
message CreateKYCReq {
string uid = 1;
string country_region = 2;
string name = 3;
string identification = 4;
string identification_type = 5;
string address = 6;
string postal_code = 7;
string id_front_image = 8;
string id_back_image = 9;
string bank_statement_img = 10;
string bank_code = 11;
string bank_name = 12;
string branch_code = 13;
string branch_name = 14;
string bank_account = 15;
}
message FindLatestKYCByUIDReq {
string uid = 1;
}
message FindKYCByIDReq {
string id = 1;
}
message ListKYCReq {
optional string uid = 1;
optional string country = 2;
optional string status = 3; // enum
int64 page_size = 4;
int64 page_index = 5;
bool sort_by_date = 6;
}
message ListKYCResp {
repeated KYC list = 1;
int64 total = 2;
}
message UpdateKYCStatusReq {
string id = 1;
string status = 2; // enumPENDING, APPROVED, REJECTED
optional string reason = 3;
}
message UpdateKYCInfoReq {
string id = 1;
optional string name = 2;
optional string identification = 3;
optional string identification_type = 4;
optional string address = 5;
optional string postal_code = 6;
optional string id_front_image = 7;
optional string id_back_image = 8;
optional string bank_statement_img = 9;
optional string bank_code = 10;
optional string bank_name = 11;
optional string branch_code = 12;
optional string branch_name = 13;
optional string bank_account = 14;
}
service Product {
// ====================== Category Service Start ======================
@ -112,5 +203,18 @@ service Product {
// ListTags tags
rpc ListTags(ListTagsReq) returns(ListTagsResp);
// ====================== Tags Service End ======================
// ====================== Know You Customer Service Start ======================
// CreateKYC KYC
rpc CreateKYC (CreateKYCReq) returns (OKResp);
// FindLatestKYCByUID 使 UID KYC
rpc FindLatestKYCByUID (FindLatestKYCByUIDReq) returns (KYC);
// FindKYCByID KYC ID
rpc FindKYCByID (FindKYCByIDReq) returns (KYC);
// ListKYC KYC
rpc ListKYC (ListKYCReq) returns (ListKYCResp);
// UpdateKYCStatus KYC
rpc UpdateKYCStatus (UpdateKYCStatusReq) returns (OKResp);
// UpdateKYCInfo 使 KYC
rpc UpdateKYCInfo (UpdateKYCInfoReq) returns (OKResp);
// ====================== Know You Customer Service End ======================
}

View File

@ -0,0 +1,53 @@
package productlogic
import (
"code.30cm.net/digimon/app-cloudep-product-service/gen_result/pb/product"
"code.30cm.net/digimon/app-cloudep-product-service/internal/svc"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/kyc"
"context"
"github.com/zeromicro/go-zero/core/logx"
)
type CreateKYCLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreateKYCLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateKYCLogic {
return &CreateKYCLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *CreateKYCLogic) CreateKYC(in *product.CreateKYCReq) (*product.OKResp, error) {
err := l.svcCtx.KYCUseCase.Create(l.ctx, &entity.KYC{
UID: in.GetUid(),
CountryRegion: in.GetCountryRegion(),
Name: in.GetName(),
Identification: in.GetIdentification(),
IdentificationType: in.GetIdentificationType(),
Address: in.GetAddress(),
PostalCode: in.GetPostalCode(),
IDFrontImage: in.GetIdFrontImage(),
IDBackImage: in.GetIdBackImage(),
BankStatementImg: in.GetBankStatementImg(),
BankCode: in.GetBankCode(),
BankName: in.GetName(),
BranchCode: in.GetBranchCode(),
BranchName: in.GetBranchName(),
BankAccount: in.GetBankAccount(),
Status: kyc.StatusPending,
RejectReason: "",
})
if err != nil {
return nil, err
}
return &product.OKResp{}, nil
}

View File

@ -0,0 +1,56 @@
package productlogic
import (
"context"
"code.30cm.net/digimon/app-cloudep-product-service/gen_result/pb/product"
"code.30cm.net/digimon/app-cloudep-product-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type FindKYCByIDLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewFindKYCByIDLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FindKYCByIDLogic {
return &FindKYCByIDLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// FindKYCByID 根據 KYC ID 查詢
func (l *FindKYCByIDLogic) FindKYCByID(in *product.FindKYCByIDReq) (*product.KYC, error) {
kycInfo, err := l.svcCtx.KYCUseCase.FindByID(l.ctx, in.GetId())
if err != nil {
return nil, err
}
return &product.KYC{
Id: kycInfo.ID.Hex(),
Uid: kycInfo.UID,
CountryRegion: kycInfo.CountryRegion,
Name: kycInfo.Name,
Identification: kycInfo.Identification,
IdentificationType: kycInfo.IdentificationType,
Address: kycInfo.Address,
PostalCode: kycInfo.PostalCode,
// 上傳文件網址(可為 object storage 的 URL
IdFrontImage: kycInfo.IDFrontImage,
IdBackImage: kycInfo.IDBackImage,
BankStatementImg: kycInfo.BankStatementImg,
BankCode: kycInfo.BankCode,
BankName: kycInfo.BankName,
BranchCode: kycInfo.BranchCode,
BranchName: kycInfo.BranchName,
BankAccount: kycInfo.BankAccount,
Status: kycInfo.Status.ToString(),
RejectReason: kycInfo.RejectReason,
UpdatedAt: kycInfo.UpdatedAt,
CreatedAt: kycInfo.CreatedAt,
}, nil
}

View File

@ -0,0 +1,56 @@
package productlogic
import (
"context"
"code.30cm.net/digimon/app-cloudep-product-service/gen_result/pb/product"
"code.30cm.net/digimon/app-cloudep-product-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type FindLatestKYCByUIDLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewFindLatestKYCByUIDLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FindLatestKYCByUIDLogic {
return &FindLatestKYCByUIDLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// FindLatestKYCByUID 根據使用者 UID 查詢最新 KYC 紀錄
func (l *FindLatestKYCByUIDLogic) FindLatestKYCByUID(in *product.FindLatestKYCByUIDReq) (*product.KYC, error) {
kycInfo, err := l.svcCtx.KYCUseCase.FindLatestByUID(l.ctx, in.GetUid())
if err != nil {
return nil, err
}
return &product.KYC{
Id: kycInfo.ID.Hex(),
Uid: kycInfo.UID,
CountryRegion: kycInfo.CountryRegion,
Name: kycInfo.Name,
Identification: kycInfo.Identification,
IdentificationType: kycInfo.IdentificationType,
Address: kycInfo.Address,
PostalCode: kycInfo.PostalCode,
// 上傳文件網址(可為 object storage 的 URL
IdFrontImage: kycInfo.IDFrontImage,
IdBackImage: kycInfo.IDBackImage,
BankStatementImg: kycInfo.BankStatementImg,
BankCode: kycInfo.BankCode,
BankName: kycInfo.BankName,
BranchCode: kycInfo.BranchCode,
BranchName: kycInfo.BranchName,
BankAccount: kycInfo.BankAccount,
Status: kycInfo.Status.ToString(),
RejectReason: kycInfo.RejectReason,
UpdatedAt: kycInfo.UpdatedAt,
CreatedAt: kycInfo.CreatedAt,
}, nil
}

View File

@ -0,0 +1,72 @@
package productlogic
import (
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/usecase"
"context"
"code.30cm.net/digimon/app-cloudep-product-service/gen_result/pb/product"
"code.30cm.net/digimon/app-cloudep-product-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type ListKYCLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewListKYCLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListKYCLogic {
return &ListKYCLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// ListKYC 分頁查詢 KYC 清單(後台審核用)
func (l *ListKYCLogic) ListKYC(in *product.ListKYCReq) (*product.ListKYCResp, error) {
filter := usecase.KYCQueryParams{
UID: in.Uid,
Country: in.Country,
Status: in.Status,
PageSize: in.GetPageSize(),
PageIndex: in.GetPageIndex(),
}
list, total, err := l.svcCtx.KYCUseCase.List(l.ctx, filter)
if err != nil {
return nil, err
}
result := make([]*product.KYC, 0, len(list))
for _, kycInfo := range list {
result = append(result, &product.KYC{
Id: kycInfo.ID.Hex(),
Uid: kycInfo.UID,
CountryRegion: kycInfo.CountryRegion,
Name: kycInfo.Name,
Identification: kycInfo.Identification,
IdentificationType: kycInfo.IdentificationType,
Address: kycInfo.Address,
PostalCode: kycInfo.PostalCode,
IdFrontImage: kycInfo.IDFrontImage,
IdBackImage: kycInfo.IDBackImage,
BankStatementImg: kycInfo.BankStatementImg,
BankCode: kycInfo.BankCode,
BankName: kycInfo.BankName,
BranchCode: kycInfo.BranchCode,
BranchName: kycInfo.BranchName,
BankAccount: kycInfo.BankAccount,
Status: kycInfo.Status.ToString(),
RejectReason: kycInfo.RejectReason,
UpdatedAt: kycInfo.UpdatedAt,
CreatedAt: kycInfo.CreatedAt,
})
}
return &product.ListKYCResp{
Total: total,
List: result,
}, nil
}

View File

@ -0,0 +1,51 @@
package productlogic
import (
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/usecase"
"context"
"google.golang.org/protobuf/proto"
"code.30cm.net/digimon/app-cloudep-product-service/gen_result/pb/product"
"code.30cm.net/digimon/app-cloudep-product-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdateKYCInfoLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateKYCInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateKYCInfoLogic {
return &UpdateKYCInfoLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// UpdateKYCInfo 更新使用者的 KYC尚未審核
func (l *UpdateKYCInfoLogic) UpdateKYCInfo(in *product.UpdateKYCInfoReq) (*product.OKResp, error) {
// 如果已完成應該不能
err := l.svcCtx.KYCUseCase.UpdateKYCInfo(l.ctx, in.GetId(), &usecase.KYCUpdateParams{
Name: proto.String(in.GetName()),
Identification: proto.String(in.GetIdentification()),
IdentificationType: proto.String(in.GetIdentificationType()),
Address: proto.String(in.GetAddress()),
PostalCode: proto.String(in.GetPostalCode()),
IDFrontImage: proto.String(in.GetIdFrontImage()),
IDBackImage: proto.String(in.GetIdBackImage()),
BankStatementImg: proto.String(in.GetBankStatementImg()),
BankCode: proto.String(in.GetBankCode()),
BankName: proto.String(in.GetBankName()),
BranchCode: proto.String(in.GetBranchCode()),
BranchName: proto.String(in.GetBranchName()),
BankAccount: proto.String(in.GetBankAccount()),
})
if err != nil {
return nil, err
}
return &product.OKResp{}, nil
}

View File

@ -0,0 +1,34 @@
package productlogic
import (
"context"
"code.30cm.net/digimon/app-cloudep-product-service/gen_result/pb/product"
"code.30cm.net/digimon/app-cloudep-product-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdateKYCStatusLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateKYCStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateKYCStatusLogic {
return &UpdateKYCStatusLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// UpdateKYCStatus 更新 KYC 審核狀態與原因
func (l *UpdateKYCStatusLogic) UpdateKYCStatus(in *product.UpdateKYCStatusReq) (*product.OKResp, error) {
err := l.svcCtx.KYCUseCase.UpdateStatus(l.ctx, in.GetId(), in.GetStatus(), in.GetReason())
if err != nil {
return nil, err
}
return &product.OKResp{}, nil
}

View File

@ -82,3 +82,39 @@ func (s *ProductServer) ListTags(ctx context.Context, in *product.ListTagsReq) (
l := productlogic.NewListTagsLogic(ctx, s.svcCtx)
return l.ListTags(in)
}
// ====================== Tags Service End ======================
func (s *ProductServer) CreateKYC(ctx context.Context, in *product.CreateKYCReq) (*product.OKResp, error) {
l := productlogic.NewCreateKYCLogic(ctx, s.svcCtx)
return l.CreateKYC(in)
}
// FindLatestKYCByUID 根據使用者 UID 查詢最新 KYC 紀錄
func (s *ProductServer) FindLatestKYCByUID(ctx context.Context, in *product.FindLatestKYCByUIDReq) (*product.KYC, error) {
l := productlogic.NewFindLatestKYCByUIDLogic(ctx, s.svcCtx)
return l.FindLatestKYCByUID(in)
}
// FindKYCByID 根據 KYC ID 查詢
func (s *ProductServer) FindKYCByID(ctx context.Context, in *product.FindKYCByIDReq) (*product.KYC, error) {
l := productlogic.NewFindKYCByIDLogic(ctx, s.svcCtx)
return l.FindKYCByID(in)
}
// ListKYC 分頁查詢 KYC 清單(後台審核用)
func (s *ProductServer) ListKYC(ctx context.Context, in *product.ListKYCReq) (*product.ListKYCResp, error) {
l := productlogic.NewListKYCLogic(ctx, s.svcCtx)
return l.ListKYC(in)
}
// UpdateKYCStatus 更新 KYC 審核狀態與原因
func (s *ProductServer) UpdateKYCStatus(ctx context.Context, in *product.UpdateKYCStatusReq) (*product.OKResp, error) {
l := productlogic.NewUpdateKYCStatusLogic(ctx, s.svcCtx)
return l.UpdateKYCStatus(in)
}
// UpdateKYCInfo 更新使用者的 KYC尚未審核
func (s *ProductServer) UpdateKYCInfo(ctx context.Context, in *product.UpdateKYCInfoReq) (*product.OKResp, error) {
l := productlogic.NewUpdateKYCInfoLogic(ctx, s.svcCtx)
return l.UpdateKYCInfo(in)
}

View File

@ -14,12 +14,14 @@ type ServiceContext struct {
Config config.Config
CategoryUseCase usecase.CategoryUseCase
TagsUseCase usecase.ProductBaseTags
KYCUseCase usecase.KYCUseCase
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
CategoryUseCase: MustCategory(c),
TagsUseCase: MustTags(c),
KYCUseCase: MustKYC(c),
Config: c,
}
}
@ -103,3 +105,43 @@ func MustTags(c config.Config) usecase.ProductBaseTags {
return uc.MustTagsUseCase(uc.TagsUseCaseParam{
TagsRepo: tagsRepo})
}
func MustKYC(c config.Config) usecase.KYCUseCase {
// 準備Mongo Config
conf := &mgo.Conf{
Schema: c.Mongo.Schema,
Host: c.Mongo.Host,
Database: c.Mongo.Database,
MaxStaleness: c.Mongo.MaxStaleness,
MaxPoolSize: c.Mongo.MaxPoolSize,
MinPoolSize: c.Mongo.MinPoolSize,
MaxConnIdleTime: c.Mongo.MaxConnIdleTime,
Compressors: c.Mongo.Compressors,
EnableStandardReadWriteSplitMode: c.Mongo.EnableStandardReadWriteSplitMode,
ConnectTimeoutMs: c.Mongo.ConnectTimeoutMs,
}
if c.Mongo.User != "" {
conf.User = c.Mongo.User
conf.Password = c.Mongo.Password
}
// 快取選項
cacheOpts := []cache.Option{
cache.WithExpiry(c.CacheExpireTime),
cache.WithNotFoundExpiry(c.CacheWithNotFoundExpiry),
}
dbOpts := []mon.Option{
mgo.SetCustomDecimalType(),
mgo.InitMongoOptions(*conf),
}
kycRepo := repo.NewKYCRepository(repo.KYCRepositoryParam{
Conf: conf,
CacheConf: c.Cache,
CacheOpts: cacheOpts,
DBOpts: dbOpts,
})
return uc.MustKYCUseCase(uc.KYCUseCaseParam{
KYCRepo: kycRepo})
}

View File

@ -38,8 +38,6 @@ type KYCUpdateParams struct {
IdentificationType *string
Address *string
PostalCode *string
DateOfBirth *string
Gender *string
IDFrontImage *string
IDBackImage *string
BankStatementImg *string

View File

@ -36,8 +36,6 @@ type KYCUpdateParams struct {
IdentificationType *string
Address *string
PostalCode *string
DateOfBirth *string
Gender *string
IDFrontImage *string
IDBackImage *string
BankStatementImg *string

View File

@ -104,11 +104,11 @@ func (repo *KYCRepository) List(ctx context.Context, params repository.KYCQueryP
// 設置排序選項
opts := options.Find().SetSkip((params.PageIndex - 1) * params.PageSize).SetLimit(params.PageSize)
if params.SortByDate {
opts.SetSort(bson.E{Key: "created_at", Value: -1})
} else {
opts.SetSort(bson.D{{Key: "updated_at", Value: -1}})
}
//if params.SortByDate {
// opts.SetSort(bson.E{Key: "created_at", Value: -1})
//} else {
// opts.SetSort(bson.D{{Key: "updated_at", Value: -1}})
//}
// 查詢符合條件的總數
total, err := repo.DB.GetClient().CountDocuments(ctx, filter)
if err != nil {
@ -164,12 +164,6 @@ func (repo *KYCRepository) UpdateKYCInfo(ctx context.Context, id string, update
if update.PostalCode != nil {
setFields["postal_code"] = *update.PostalCode
}
if update.DateOfBirth != nil {
setFields["date_of_birth"] = *update.DateOfBirth
}
if update.Gender != nil {
setFields["gender"] = *update.Gender
}
if update.IDFrontImage != nil {
setFields["id_front_image"] = *update.IDFrontImage
}

View File

@ -48,7 +48,7 @@ func (use *KYCUseCase) Create(ctx context.Context, data *entity.KYC) error {
{Key: "param", Value: data},
{Key: "func", Value: "KYCRepo.FindLatestByUID"},
{Key: "reason", Value: "KYC already in progress or approved"},
}, "不能重複送出 KYC 資料")
}, "KYC already in progress or approved")
}
// ✅ 若查不到資料ErrNotFound或前一筆是 REJECTED允許建立
@ -68,6 +68,10 @@ func (use *KYCUseCase) Create(ctx context.Context, data *entity.KYC) error {
func (use *KYCUseCase) FindLatestByUID(ctx context.Context, uid string) (*entity.KYC, error) {
latest, err := use.KYCRepo.FindLatestByUID(ctx, uid)
if errors.Is(err, repo.ErrNotFound) {
return nil, errs.ResourceNotFound("failed to find latest kyc")
}
if err != nil {
return nil, errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
@ -144,8 +148,6 @@ func (use *KYCUseCase) UpdateKYCInfo(ctx context.Context, id string, update *use
IdentificationType: update.IdentificationType,
Address: update.Address,
PostalCode: update.PostalCode,
DateOfBirth: update.DateOfBirth,
Gender: update.Gender,
IDFrontImage: update.IDFrontImage,
IDBackImage: update.IDBackImage,
BankStatementImg: update.BankStatementImg,

View File

@ -82,7 +82,7 @@ func TestKYCUseCase_Create(t *testing.T) {
{Key: "func", Value: "KYCRepo.FindLatestByUID"},
{Key: "reason", Value: "KYC already in progress or approved"},
},
"不能重複送出 KYC 資料",
"KYC already in progress or approved",
),
},
{
@ -473,8 +473,6 @@ func TestKYCUseCase_UpdateKYCInfo(t *testing.T) {
IdentificationType: proto.String("passport"),
Address: proto.String("Taipei City"),
PostalCode: proto.String("100"),
DateOfBirth: proto.String("1993-04-17"),
Gender: proto.String("M"),
IDFrontImage: proto.String("https://example.com/front.jpg"),
IDBackImage: proto.String("https://example.com/back.jpg"),
BankStatementImg: proto.String("https://example.com/bank.jpg"),
@ -503,8 +501,6 @@ func TestKYCUseCase_UpdateKYCInfo(t *testing.T) {
IdentificationType: updateParams.IdentificationType,
Address: updateParams.Address,
PostalCode: updateParams.PostalCode,
DateOfBirth: updateParams.DateOfBirth,
Gender: updateParams.Gender,
IDFrontImage: updateParams.IDFrontImage,
IDBackImage: updateParams.IDBackImage,
BankStatementImg: updateParams.BankStatementImg,