Files

461 lines
19 KiB
Go
Raw Permalink Normal View History

package command
import (
"fmt"
"io"
"os"
"strings"
dto "code.zhecent.com/open/shop-crm-agent/internal/api"
crmClient "code.zhecent.com/open/shop-crm-agent/internal/client"
runtimeConfig "code.zhecent.com/open/shop-crm-agent/internal/config"
"github.com/spf13/cobra"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
type crmAgentWriteOptions struct{ requestID string }
func New(version string) *cobra.Command {
cmd := &cobra.Command{
Use: "shop-crm-agent",
Short: "通过 Protobuf API 操作 Shop CRM",
Version: version,
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error { return cmd.Help() },
}
cmd.AddCommand(newInitCmd(), newCrmAgentCustomerListCmd(), newCrmAgentProjectCmd(), newCrmAgentQuotationCmd(), newCrmAgentCatalogCmd(), newCrmAgentEditCmd())
return cmd
}
func newCrmAgentHTTPClient() (*crmClient.Client, error) {
configuration, err := runtimeConfig.Load()
if err != nil {
return nil, err
}
return crmClient.New(configuration.APIBaseURL, configuration.Token, nil)
}
func writeCrmAgentProto(writer io.Writer, message proto.Message) error {
data, err := (protojson.MarshalOptions{Indent: " ", EmitUnpopulated: true}).Marshal(message)
if err != nil {
return err
}
_, err = fmt.Fprintln(writer, string(data))
return err
}
type crmAgentHeaderResponse interface {
proto.Message
GetHeader() *dto.ResponseHeader
}
func writeCrmAgentResult(writer io.Writer, response crmAgentHeaderResponse, commandErr error) error {
if response != nil && response.GetHeader() != nil {
if err := writeCrmAgentProto(writer, response); err != nil {
return err
}
}
return commandErr
}
func printCrmAgentResult(response crmAgentHeaderResponse, commandErr error) error {
return writeCrmAgentResult(os.Stdout, response, commandErr)
}
func bindCrmAgentWrite(cmd *cobra.Command, options *crmAgentWriteOptions) {
cmd.Flags().StringVar(&options.requestID, "request-id", "", "8-80位幂等请求 ID")
_ = cmd.MarkFlagRequired("request-id")
}
func crmAgentCommandHeader(options crmAgentWriteOptions) *dto.CommandHeader {
return &dto.CommandHeader{RequestId: strings.TrimSpace(options.requestID)}
}
func newCrmAgentCustomerListCmd() *cobra.Command {
var keyword string
var page, pageSize int32
cmd := &cobra.Command{Use: "customer-list", Short: "查询创建项目可引用的 CRM 客户", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.CustomerList(cmd.Context(), &dto.CustomerListRequest{Keyword: keyword, Page: page, PageSize: pageSize})
return printCrmAgentResult(response, err)
}}
cmd.Flags().StringVar(&keyword, "keyword", "", "客户姓名或手机号")
cmd.Flags().Int32Var(&page, "page", 1, "页码")
cmd.Flags().Int32Var(&pageSize, "page-size", 20, "每页数量")
return cmd
}
func newCrmAgentProjectCmd() *cobra.Command {
cmd := &cobra.Command{Use: "project", Short: "查询和维护 CRM 项目", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return cmd.Help() }}
cmd.AddCommand(newCrmAgentProjectListCmd(), newCrmAgentProjectGetCmd(), newCrmAgentProjectCreateCmd(), newCrmAgentProjectUpdateCmd(), newCrmAgentProjectStatusCmd(), newCrmAgentProjectDeletePreviewCmd(), newCrmAgentProjectDeleteCmd())
return cmd
}
func newCrmAgentProjectListCmd() *cobra.Command {
var keyword, status, houseType string
var page, pageSize int32
cmd := &cobra.Command{Use: "list", Short: "分页查询项目", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.ProjectList(cmd.Context(), &dto.ProjectListRequest{Keyword: keyword, Status: status, HouseType: houseType, Page: page, PageSize: pageSize})
return printCrmAgentResult(response, err)
}}
cmd.Flags().StringVar(&keyword, "keyword", "", "项目关键词")
cmd.Flags().StringVar(&status, "status", "", "项目状态")
cmd.Flags().StringVar(&houseType, "house-type", "", "房屋类型")
cmd.Flags().Int32Var(&page, "page", 1, "页码")
cmd.Flags().Int32Var(&pageSize, "page-size", 20, "每页数量")
return cmd
}
func newCrmAgentProjectGetCmd() *cobra.Command {
var projectID uint64
cmd := &cobra.Command{Use: "get", Short: "读取项目详情", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.ProjectGet(cmd.Context(), &dto.ProjectGetRequest{ProjectId: projectID})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&projectID, "project-id", 0, "项目 ID")
_ = cmd.MarkFlagRequired("project-id")
return cmd
}
type crmAgentProjectFields struct {
name string
customerID uint64
houseType, province, provinceCode, city, cityCode, district, districtCode, address, remark string
}
func bindCrmAgentProjectFields(cmd *cobra.Command, fields *crmAgentProjectFields, includeCustomer, requireCore bool) {
cmd.Flags().StringVar(&fields.name, "name", "", "项目名称")
cmd.Flags().StringVar(&fields.houseType, "house-type", "", "房屋类型: flat/duplex/villa/commercial/other")
if includeCustomer {
cmd.Flags().Uint64Var(&fields.customerID, "customer-id", 0, "客户 ID")
_ = cmd.MarkFlagRequired("customer-id")
}
cmd.Flags().StringVar(&fields.province, "province", "", "省份")
cmd.Flags().StringVar(&fields.provinceCode, "province-code", "", "省份编码")
cmd.Flags().StringVar(&fields.city, "city", "", "城市")
cmd.Flags().StringVar(&fields.cityCode, "city-code", "", "城市编码")
cmd.Flags().StringVar(&fields.district, "district", "", "区县")
cmd.Flags().StringVar(&fields.districtCode, "district-code", "", "区县编码")
cmd.Flags().StringVar(&fields.address, "address", "", "详细地址")
cmd.Flags().StringVar(&fields.remark, "remark", "", "备注")
if requireCore {
_ = cmd.MarkFlagRequired("name")
_ = cmd.MarkFlagRequired("house-type")
}
}
func newCrmAgentProjectCreateCmd() *cobra.Command {
var fields crmAgentProjectFields
var write crmAgentWriteOptions
cmd := &cobra.Command{Use: "create", Short: "创建项目", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.ProjectCreate(cmd.Context(), &dto.ProjectCreateRequest{Command: crmAgentCommandHeader(write), Name: fields.name, CustomerId: fields.customerID, HouseType: fields.houseType, Province: fields.province, ProvinceCode: fields.provinceCode, City: fields.city, CityCode: fields.cityCode, District: fields.district, DistrictCode: fields.districtCode, Address: fields.address, Remark: fields.remark})
return printCrmAgentResult(response, err)
}}
bindCrmAgentProjectFields(cmd, &fields, true, true)
bindCrmAgentWrite(cmd, &write)
return cmd
}
func newCrmAgentProjectUpdateCmd() *cobra.Command {
var projectID uint64
var fields crmAgentProjectFields
var write crmAgentWriteOptions
cmd := &cobra.Command{Use: "update", Short: "更新项目资料", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.ProjectUpdate(cmd.Context(), &dto.ProjectUpdateRequest{
Command: crmAgentCommandHeader(write), ProjectId: projectID,
Name: crmAgentChangedString(cmd, "name", fields.name), HouseType: crmAgentChangedString(cmd, "house-type", fields.houseType),
Province: crmAgentChangedString(cmd, "province", fields.province), ProvinceCode: crmAgentChangedString(cmd, "province-code", fields.provinceCode),
City: crmAgentChangedString(cmd, "city", fields.city), CityCode: crmAgentChangedString(cmd, "city-code", fields.cityCode),
District: crmAgentChangedString(cmd, "district", fields.district), DistrictCode: crmAgentChangedString(cmd, "district-code", fields.districtCode),
Address: crmAgentChangedString(cmd, "address", fields.address),
Remark: crmAgentChangedString(cmd, "remark", fields.remark),
})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&projectID, "project-id", 0, "项目 ID")
_ = cmd.MarkFlagRequired("project-id")
bindCrmAgentProjectFields(cmd, &fields, false, false)
bindCrmAgentWrite(cmd, &write)
return cmd
}
func crmAgentChangedString(cmd *cobra.Command, flagName, value string) *string {
if !cmd.Flags().Changed(flagName) {
return nil
}
return &value
}
func newCrmAgentProjectStatusCmd() *cobra.Command {
var projectID uint64
var status string
var write crmAgentWriteOptions
cmd := &cobra.Command{Use: "status", Short: "修改项目状态", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.ProjectStatus(cmd.Context(), &dto.ProjectStatusRequest{Command: crmAgentCommandHeader(write), ProjectId: projectID, Status: status})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&projectID, "project-id", 0, "项目 ID")
cmd.Flags().StringVar(&status, "status", "", "状态: active/completed/shelved")
_ = cmd.MarkFlagRequired("project-id")
_ = cmd.MarkFlagRequired("status")
bindCrmAgentWrite(cmd, &write)
return cmd
}
func newCrmAgentProjectDeletePreviewCmd() *cobra.Command {
var projectID uint64
cmd := &cobra.Command{Use: "delete-preview", Short: "预览空项目删除", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.ProjectDeletePreview(cmd.Context(), &dto.ProjectDeletePreviewRequest{ProjectId: projectID})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&projectID, "project-id", 0, "项目 ID")
_ = cmd.MarkFlagRequired("project-id")
return cmd
}
func newCrmAgentProjectDeleteCmd() *cobra.Command {
var projectID uint64
var changeSet string
var write crmAgentWriteOptions
cmd := &cobra.Command{Use: "delete", Short: "删除已预览的空项目", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.ProjectDelete(cmd.Context(), &dto.ProjectDeleteRequest{Command: crmAgentCommandHeader(write), ProjectId: projectID, ChangeSetId: changeSet})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&projectID, "project-id", 0, "项目 ID")
cmd.Flags().StringVar(&changeSet, "change-set-id", "", "delete-preview 返回的 changeSetId")
_ = cmd.MarkFlagRequired("project-id")
_ = cmd.MarkFlagRequired("change-set-id")
bindCrmAgentWrite(cmd, &write)
return cmd
}
func newCrmAgentQuotationCmd() *cobra.Command {
cmd := &cobra.Command{Use: "quotation", Short: "查询和维护独立报价", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return cmd.Help() }}
cmd.AddCommand(newCrmAgentQuotationListCmd(), newCrmAgentQuotationGetCmd(), newCrmAgentQuotationCreateCmd(), newCrmAgentQuotationCopyCmd(), newCrmAgentQuotationTitleCmd(), newCrmAgentQuotationStatusCmd(), newCrmAgentQuotationLockCmd(true), newCrmAgentQuotationLockCmd(false), newCrmAgentQuotationDeletePreviewCmd(), newCrmAgentQuotationDeleteCmd())
return cmd
}
func newCrmAgentQuotationListCmd() *cobra.Command {
var projectID uint64
var keyword, status string
var page, pageSize int32
cmd := &cobra.Command{Use: "list", Short: "分页查询报价", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.QuotationList(cmd.Context(), &dto.QuotationListRequest{ProjectId: projectID, Keyword: keyword, Status: status, Page: page, PageSize: pageSize})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&projectID, "project-id", 0, "项目 ID")
cmd.Flags().StringVar(&keyword, "keyword", "", "关键词")
cmd.Flags().StringVar(&status, "status", "", "报价状态")
cmd.Flags().Int32Var(&page, "page", 1, "页码")
cmd.Flags().Int32Var(&pageSize, "page-size", 20, "每页数量")
return cmd
}
func newCrmAgentQuotationGetCmd() *cobra.Command {
var id uint64
cmd := &cobra.Command{Use: "get", Short: "读取报价详情", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.QuotationGet(cmd.Context(), &dto.QuotationGetRequest{QuotationId: id})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&id, "quotation-id", 0, "报价 ID")
_ = cmd.MarkFlagRequired("quotation-id")
return cmd
}
func newCrmAgentQuotationCreateCmd() *cobra.Command {
var projectID uint64
var title string
var write crmAgentWriteOptions
cmd := &cobra.Command{Use: "create", Short: "创建空报价", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.QuotationCreate(cmd.Context(), &dto.QuotationCreateRequest{Command: crmAgentCommandHeader(write), ProjectId: projectID, Title: title})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&projectID, "project-id", 0, "项目 ID")
cmd.Flags().StringVar(&title, "title", "报价", "报价标题")
_ = cmd.MarkFlagRequired("project-id")
bindCrmAgentWrite(cmd, &write)
return cmd
}
func newCrmAgentQuotationCopyCmd() *cobra.Command {
var id uint64
var title string
var write crmAgentWriteOptions
cmd := &cobra.Command{Use: "copy", Short: "复制独立报价", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.QuotationCopy(cmd.Context(), &dto.QuotationCopyRequest{Command: crmAgentCommandHeader(write), QuotationId: id, Title: title})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&id, "quotation-id", 0, "源报价 ID")
cmd.Flags().StringVar(&title, "title", "", "副本标题")
_ = cmd.MarkFlagRequired("quotation-id")
_ = cmd.MarkFlagRequired("title")
bindCrmAgentWrite(cmd, &write)
return cmd
}
func newCrmAgentQuotationTitleCmd() *cobra.Command {
var id uint64
var title string
var write crmAgentWriteOptions
cmd := &cobra.Command{Use: "title", Short: "修改报价标题", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.QuotationTitle(cmd.Context(), &dto.QuotationTitleRequest{Command: crmAgentCommandHeader(write), QuotationId: id, Title: title})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&id, "quotation-id", 0, "报价 ID")
cmd.Flags().StringVar(&title, "title", "", "标题")
_ = cmd.MarkFlagRequired("quotation-id")
_ = cmd.MarkFlagRequired("title")
bindCrmAgentWrite(cmd, &write)
return cmd
}
func newCrmAgentQuotationStatusCmd() *cobra.Command {
var id uint64
var status string
var write crmAgentWriteOptions
cmd := &cobra.Command{Use: "status", Short: "修改报价状态", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.QuotationStatus(cmd.Context(), &dto.QuotationStatusRequest{Command: crmAgentCommandHeader(write), QuotationId: id, Status: status})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&id, "quotation-id", 0, "报价 ID")
cmd.Flags().StringVar(&status, "status", "", "报价状态")
_ = cmd.MarkFlagRequired("quotation-id")
_ = cmd.MarkFlagRequired("status")
bindCrmAgentWrite(cmd, &write)
return cmd
}
func newCrmAgentQuotationLockCmd(locked bool) *cobra.Command {
use, short := "unlock", "解锁报价"
if locked {
use, short = "lock", "锁定报价"
}
var id uint64
var write crmAgentWriteOptions
cmd := &cobra.Command{Use: use, Short: short, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
request := &dto.QuotationLockRequest{Command: crmAgentCommandHeader(write), QuotationId: id}
var response *dto.QuotationResponse
if locked {
response, err = client.QuotationLock(cmd.Context(), request)
} else {
response, err = client.QuotationUnlock(cmd.Context(), request)
}
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&id, "quotation-id", 0, "报价 ID")
_ = cmd.MarkFlagRequired("quotation-id")
bindCrmAgentWrite(cmd, &write)
return cmd
}
func newCrmAgentQuotationDeletePreviewCmd() *cobra.Command {
var id uint64
cmd := &cobra.Command{Use: "delete-preview", Short: "预览报价删除", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.QuotationDeletePreview(cmd.Context(), &dto.QuotationDeletePreviewRequest{QuotationId: id})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&id, "quotation-id", 0, "报价 ID")
_ = cmd.MarkFlagRequired("quotation-id")
return cmd
}
func newCrmAgentQuotationDeleteCmd() *cobra.Command {
var id uint64
var revision int32
var changeSet string
var write crmAgentWriteOptions
cmd := &cobra.Command{Use: "delete", Short: "按 revision 删除报价", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.QuotationDelete(cmd.Context(), &dto.QuotationDeleteRequest{Command: crmAgentCommandHeader(write), QuotationId: id, Revision: revision, ChangeSetId: changeSet})
return printCrmAgentResult(response, err)
}}
cmd.Flags().Uint64Var(&id, "quotation-id", 0, "报价 ID")
cmd.Flags().Int32Var(&revision, "revision", 0, "delete-preview 返回的 revision")
cmd.Flags().StringVar(&changeSet, "change-set-id", "", "delete-preview 返回的 changeSetId")
_ = cmd.MarkFlagRequired("quotation-id")
_ = cmd.MarkFlagRequired("revision")
_ = cmd.MarkFlagRequired("change-set-id")
bindCrmAgentWrite(cmd, &write)
return cmd
}
func newCrmAgentCatalogCmd() *cobra.Command {
var keyword string
var categoryID, rootCategoryID, brandID uint64
var page, pageSize int32
cmd := &cobra.Command{Use: "catalog-search", Short: "查询可加入报价的产品库 SKU", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error {
client, err := newCrmAgentHTTPClient()
if err != nil {
return err
}
response, err := client.CatalogSearch(cmd.Context(), &dto.CatalogSearchRequest{Keyword: keyword, CategoryId: categoryID, RootCategoryId: rootCategoryID, BrandId: brandID, Page: page, PageSize: pageSize})
return printCrmAgentResult(response, err)
}}
cmd.Flags().StringVar(&keyword, "keyword", "", "关键词")
cmd.Flags().Uint64Var(&categoryID, "category-id", 0, "分类 ID")
cmd.Flags().Uint64Var(&rootCategoryID, "root-category-id", 0, "根分类 ID")
cmd.Flags().Uint64Var(&brandID, "brand-id", 0, "品牌 ID")
cmd.Flags().Int32Var(&page, "page", 1, "页码")
cmd.Flags().Int32Var(&pageSize, "page-size", 20, "每页数量")
return cmd
}