Compare commits

...

30 Commits

Author SHA1 Message Date
cjy
1572be00a2 调整GetPendingAssignBySubNums与GetArtistUploadStatsList的BundleOrder过滤规则
- taskStatsQueryOptions 改写:
  * 普通任务分支改为 Case A OR Case B:
    - Case A: order_type=1 AND contract_tpl_type=1 AND status=2 (套餐-普通,仅已签已支付)
    - Case B: order_type=2 AND contract_tpl_type=2 AND status IN (1,2,4) AND pay_later_status IN (1,2) (套餐先用后付存量)
  * 先用后付任务分支保持不变: order_type=2 AND contract_tpl_type=3 AND pay_later_status IN (1,2) AND status IN (1,2,4)
  * 去掉对 order_mode / IFNULL 的依赖,改用 order_type + contract_tpl_type 区分
  * NOT EXISTS 内层 bor2 谓词与外层完全对齐,保持"每用户取最新单"语义
2026-06-11 13:05:04 +08:00
cjy
6d5b96a7d0 优化GetPendingAssignBySubNums查询速度 2026-06-11 10:09:40 +08:00
cjy
590f0dedd6 test: 为 taskDao 与 taskLogic 添加单元测试
新增 internal/testutil/db.go,基于 DATA-DOG/go-sqlmock 提供
SetupTaskBenchDB 帮助函数,把 mock 出来的 *gorm.DB 挂到
app.ModuleClients.TaskBenchDB 上,方便 dao / logic 层单测。
SQL 匹配使用 QueryMatcherRegexp,避免对 gorm 生成的 SQL 字面量
(字段顺序、占位符数量等)过度耦合。

- internal/dao/taskDao_test.go:覆盖 GetPendingTaskLayout 等
  DAO 方法的 SQL 行为与错误分支。
- internal/logic/taskLogic_test.go:覆盖 CreateTaskWorkLog
  的入参范围校验分支。

go.mod / go.sum:
- 新增直接依赖 github.com/DATA-DOG/go-sqlmock v1.5.2、
  github.com/stretchr/testify v1.11.1;
- 调整 google/uuid、robfig/cron/v3、satori/go.uuid 为直接依赖。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 15:21:03 +08:00
cjy
038f07f5af fix: 优化任务管理台列表的查询速度 2026-06-10 14:26:51 +08:00
cjy
9aa2bf6459 fix: 根据视频和视频脚本里面的uuid来判断每个视频脚本属于哪个一个增值套餐的里面,避免订单逾期未支付导致统计上传视频数量错误的问题 2026-06-10 11:00:49 +08:00
cjy
d94afe2ab7 fix:修改字段错误 2026-06-09 13:25:47 +08:00
cjy
d8be4ec11d 更新pb 2026-06-09 12:28:21 +08:00
cjy
8a19225eb5 Update:任务台订单过滤按 contract_tpl_type 拆分支
- 先用后付分支:仅取 contract_tpl_type=3 的增值先用后付订单
- 普通任务分支:追加 contract_tpl_type=2 的套餐先用后付主订单
- latestOrderFilter 同步纳入两类订单的「较新订单」比较
- 修正 06fb278 中「套餐先用后付主订单被错算成先用后付任务」的问题
2026-06-09 12:26:24 +08:00
cjy
06fb278dcc Update:任务管理台统计接入先用后付维度
- proto/dto: TaskQueryRequest、BatchAssignTaskItem、PendingAssignRequest
  新增 bundle_task_type 字段;dto 拆出 PendingAssignQueryRequest
- 艺人上传统计/可指派数量查询:抽出 target_orders + active_order_balances
  作为共享 CTE,普通套餐仍按"每用户最新一单"过滤,先用后付按
  pay_later_status IN (1,2) 取全部有效单,并在 active_windows 里
  按 user_id 聚合多条先用后付订单的配额
- cast_work(视频)和 cast_video_script 统计按 billing_type 过滤,
  task_assign_records / task_management 按 bundle_task_type 过滤,
  AssignTask / BatchAssignTasks / CompleteTaskManually /
  GetArtistTaskStatsBySubNum 全部加入 bundle_task_type 维度
- 顺手修 pkg/db/mysql.go 里 fmt.Println 用了 %s 占位符不生效的问题
2026-06-09 10:28:51 +08:00
cjy
0a1878e5ae Merge branch 'jng' into feat-cjy-Paylater
# Conflicts:
#	internal/logic/bundleLogic.go
#	pb/bundle/bundle.pb.go
#	pb/bundle/bundle_triple.pb.go
2026-06-09 09:38:13 +08:00
cjy
4a867d2a97 Merge branch 'feat-hjj-Paylater' into feat-cjy-Paylater
# Conflicts:
#	pb/bundle/bundle.pb.go
2026-06-09 09:31:33 +08:00
jiaji.H
6a313329e8 Update:增加订单过滤插叙 2026-06-08 19:25:54 +08:00
jiaji.H
46cea10a5e Update:增加查询指定用户未过期的订单列表 2026-06-08 19:17:52 +08:00
JNG
d18a5243f2 订单新增先用后付 2026-06-08 17:08:04 +08:00
cjy
c4e525dc24 update: 更新cast_work表 2026-06-08 16:23:13 +08:00
cjy
36a6e82fae update: 更新cast_work表和video_script 表结构 2026-06-08 16:16:39 +08:00
jiaji.H
307905849b Update 2026-06-08 14:04:31 +08:00
jiaji.H
17c8114b47 Update:增加字段 2026-06-08 11:51:31 +08:00
cjy
a6a9b9197c fix: 修改zap记录日志错误的问题 2026-06-05 14:44:07 +08:00
jiaji.H
f9c7edf7c3 Update:增加字段赋值 2026-06-05 13:36:32 +08:00
jiaji.H
20f94743d4 Merge branch 'feat-hjj-Paylater' of http://gitea.tools.fontree.cn:3000/fiee/micro-bundle into feat-hjj-Paylater 2026-06-05 11:02:00 +08:00
jiaji.H
d668424ac7 Update:增加对应类型 2026-06-05 10:58:40 +08:00
cjy
235bea33ff Update:任务工作日志接入bundleTaskType,支持写入/查询过滤/返回 2026-06-04 16:05:11 +08:00
jiaji.H
f2eb757c67 Update 2026-06-04 15:40:27 +08:00
cjy
239b97b759 Update:任务管理台接入bundleTaskType,支持指派时设置与查询过滤
- proto: 新增 enum BundleTaskType,4 个相关 message 加字段
- model: 新增 BundleTaskTypeNormal/PayLater 常量
- dto: TaskAssignRequest / EmployeeTaskQueryRequest /
  TaskAssignRecordsQueryRequest / TaskAssignRecordsResponse
  新增 BundleTaskType 字段
- controller: 3 个方法做 proto→dto 映射,响应带回该字段
- logic: 3 个查询返回路径的 DTO 映射补字段
- dao: AssignTask 写入字段(新建/更新 task_management,
  新建 task_assign_records);GetEmployeeAssignedTasks 的 Select
  加列、Where 加条件;GetTaskAssignRecordsList 列表与汇总查询
  两处都加 Where 条件
2026-06-04 15:37:50 +08:00
jiaji.H
acdd67e0ae Update:增加套餐类型 2026-06-04 14:26:32 +08:00
cjy
89c1582bac Update:任务管理台三表新增bundle_task_type字段,支持先用后付任务区分 2026-06-04 14:15:56 +08:00
jiaji.H
2fd6a2429c Update:更新字段 2026-06-03 17:09:53 +08:00
jiaji.H
7df0804b3d Update:调整字段位置 2026-06-03 17:00:00 +08:00
jiaji.H
ffbb174857 Update:增加字段 2026-06-03 16:45:55 +08:00
32 changed files with 8993 additions and 6433 deletions

View File

@ -5,7 +5,7 @@ import (
"micro-bundle/internal/controller"
_ "micro-bundle/internal/handler"
"micro-bundle/pkg/app"
"micro-bundle/pkg/cron"
// "micro-bundle/pkg/cron" // 定时任务已迁移至客户端 fonchain-fiee
"micro-bundle/pkg/db"
"micro-bundle/pkg/tracing"
@ -49,7 +49,7 @@ func main() {
if err = config.Load(); err != nil {
panic(err)
}
cron.InitCronJob() // 定时任务
// cron.InitCronJob() // 定时任务(先用后付到期扫描已迁移至客户端 fonchain-fiee 的 cron 执行,通过 MarkOverdueOrders RPC 调用)
select {}
// dao.AddBundleExtendRecord(model.BundleExtensionRecords{
// UserId: 57,

11
go.mod
View File

@ -4,6 +4,7 @@ go 1.23.3
require (
dubbo.apache.org/dubbo-go/v3 v3.0.2
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704
github.com/bwmarrin/snowflake v0.3.0
github.com/dubbogo/grpc-go v1.42.9
@ -13,18 +14,21 @@ require (
github.com/gin-gonic/gin v1.9.0
github.com/go-redis/redis v6.15.9+incompatible
github.com/golang/protobuf v1.5.2
github.com/google/uuid v1.6.0
github.com/google/wire v0.6.0
github.com/jinzhu/copier v0.3.5
github.com/mwitkow/go-proto-validators v0.3.2
github.com/natefinch/lumberjack v2.0.0+incompatible
github.com/opentracing/opentracing-go v1.2.0
github.com/robfig/cron/v3 v3.0.1
github.com/samber/lo v1.51.0
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
github.com/shopspring/decimal v1.4.0
github.com/spf13/viper v1.7.1
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271
github.com/stretchr/testify v1.11.1
github.com/uber/jaeger-client-go v2.30.0+incompatible
go.uber.org/zap v1.24.0
google.golang.org/grpc v1.54.0
google.golang.org/protobuf v1.29.1
gorm.io/datatypes v1.2.5
gorm.io/driver/mysql v1.5.6
@ -81,7 +85,6 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
@ -114,6 +117,7 @@ require (
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/polarismesh/polaris-go v1.1.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_golang v1.12.2 // indirect
@ -121,8 +125,6 @@ require (
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/statsd_exporter v0.21.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b // indirect
github.com/shirou/gopsutil/v3 v3.22.2 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.9.2 // indirect
@ -154,6 +156,7 @@ require (
golang.org/x/text v0.22.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.54.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

27
go.sum
View File

@ -49,7 +49,10 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
@ -251,6 +254,7 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
@ -294,6 +298,7 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
@ -329,7 +334,9 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
@ -396,6 +403,7 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -522,9 +530,13 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
@ -572,6 +584,7 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/knadh/koanf v1.4.1/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO43gs=
@ -625,10 +638,12 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
@ -719,6 +734,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -813,6 +829,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@ -852,8 +869,9 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -863,7 +881,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
@ -1252,6 +1271,7 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -1503,9 +1523,12 @@ gorm.io/datatypes v1.2.5/go.mod h1:I5FUdlKpLb5PMqeMQhm30CQ6jXP8Rj89xkTeCSAaAD4=
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U=
gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A=
gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g=
gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g=
gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.23.0/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=

View File

@ -58,6 +58,16 @@ func (b *BundleProvider) GetInEffectOrderRecord(_ context.Context, req *bundle.G
return logic.GetInEffectOrderRecord(req)
}
// CheckOrderEligibility 下单前置校验(先用后付)
func (b *BundleProvider) CheckOrderEligibility(_ context.Context, req *bundle.CheckOrderEligibilityRequest) (res *bundle.CheckOrderEligibilityResponse, err error) {
return logic.CheckOrderEligibility(req)
}
// MarkOverdueOrders 扫描到期订单(兜底接口,可由外部定时任务调用)
func (b *BundleProvider) MarkOverdueOrders(_ context.Context, req *bundle.MarkOverdueOrdersRequest) (res *bundle.MarkOverdueOrdersResponse, err error) {
return logic.MarkOverdueOrders(req)
}
// 增值套餐相关
func (b *BundleProvider) CreateValueAddBundle(_ context.Context, req *bundle.CreateValueAddBundleRequest) (res *bundle.CreateValueAddBundleResponse, err error) {
if err = req.Validate(); err != nil {

View File

@ -27,6 +27,11 @@ func (b *BundleProvider) GetBundleBalanceByUserId(_ context.Context, req *bundle
return logic.GetBundleBalanceByUserId(req)
}
// 根据用户ID查询套餐订单列表过期的套餐订单不展示
func (b *BundleProvider) GetBundleOrderListByUserId(_ context.Context, req *bundle.GetBundleOrderListByUserIdReq) (*bundle.GetBundleOrderListByUserIdResp, error) {
return logic.GetBundleOrderListByUserId(req)
}
func (b *BundleProvider) GetBundleBalanceByOrderUUID(_ context.Context, req *bundle.GetBundleBalanceByOrderUUIDReq) (*bundle.GetBundleBalanceByOrderUUIDResp, error) {
startTime, status, err := logic.GetBundleBalanceByOrderUUID(req.OrderUUID)
if err != nil {

View File

@ -30,6 +30,7 @@ func (b *BundleProvider) AssignTask(_ context.Context, req *bundle.TaskAssignReq
AssignVideoScriptCount: int(req.AssignVideoScriptCount),
AssignReportCount: int(req.AssignReportCount),
UserID: int(req.UserID),
BundleTaskType: int(req.BundleTaskType),
}
err := logic.AssignTask(daoReq)
@ -82,6 +83,7 @@ func (b *BundleProvider) GetEmployeeAssignedTasks(_ context.Context, req *bundle
Page: int(req.Page),
PageSize: int(req.PageSize),
TaskBatch: req.TaskBatch,
BundleTaskType: int(req.BundleTaskType),
}
records, total, err := logic.GetEmployeeAssignedTasks(daoReq)
@ -195,19 +197,20 @@ func (b *BundleProvider) GetTaskAssignRecordsList(_ context.Context, req *bundle
req.SortBy = sortBy
}
daoReq := &dto.TaskAssignRecordsQueryRequest{
Keyword: req.Keyword,
TaskAssignee: req.TaskAssignee,
Operator: req.Operator,
OperatorNum: req.OperatorNum,
StartTime: req.StartTime,
EndTime: req.EndTime,
Status: int(req.Status),
ActualStatus: int(req.ActualStatus),
Page: int(req.Page),
PageSize: int(req.PageSize),
TaskBatch: req.TaskBatch,
SortBy: req.SortBy,
SortType: req.SortType,
Keyword: req.Keyword,
TaskAssignee: req.TaskAssignee,
Operator: req.Operator,
OperatorNum: req.OperatorNum,
StartTime: req.StartTime,
EndTime: req.EndTime,
Status: int(req.Status),
ActualStatus: int(req.ActualStatus),
Page: int(req.Page),
PageSize: int(req.PageSize),
TaskBatch: req.TaskBatch,
SortBy: req.SortBy,
SortType: req.SortType,
BundleTaskType: int(req.BundleTaskType),
}
// 调用logic层
@ -264,6 +267,7 @@ func convertToTaskAssignRecordInfo(record *dto.TaskAssignRecordsResponse) *bundl
CompleteDataCount: int32(record.CompleteDataCount),
PendingReportCount: int32(record.PendingReportCount),
CompleteReportCount: int32(record.CompleteReportCount),
BundleTaskType: bundle.BundleTaskType(record.BundleTaskType),
}
}
@ -314,6 +318,7 @@ func (b *BundleProvider) BatchAssignTask(_ context.Context, req *bundle.BatchAss
AssignReportCount: int(it.AssignReportCount),
TaskBatch: it.TaskBatch,
UserID: int(it.UserID),
BundleTaskType: int(it.BundleTaskType),
})
}
@ -339,6 +344,7 @@ func (b *BundleProvider) GetArtistUploadStatsList(_ context.Context, req *bundle
SortType: req.SortType,
LastTaskAssignee: req.LastTaskAssignee,
SubNums: req.SubNums,
BundleTaskType: int(req.BundleTaskType),
}
items, total, err := logic.GetArtistUploadStatsList(daoReq)
@ -410,7 +416,12 @@ func (b *BundleProvider) GetArtistUploadStatsList(_ context.Context, req *bundle
// GetPendingAssign 查询艺人可指派数量
func (b *BundleProvider) GetPendingAssign(_ context.Context, req *bundle.PendingAssignRequest) (*bundle.PendingAssignResponse, error) {
items, total, err := logic.GetPendingAssignBySubNums(req.SubNums, int(req.Page), int(req.PageSize))
items, total, err := logic.GetPendingAssignBySubNums(&dto.PendingAssignQueryRequest{
SubNums: req.SubNums,
Page: int(req.Page),
PageSize: int(req.PageSize),
BundleTaskType: int(req.BundleTaskType),
})
if err != nil {
return nil, err
}
@ -460,6 +471,7 @@ func (b *BundleProvider) GetTaskWorkLogList(_ context.Context, req *bundle.TaskW
ArtistUUID: req.ArtistUUID,
SubNum: req.SubNum,
ArtistName: req.ArtistName,
BundleTaskType: int(req.BundleTaskType),
Page: int(req.Page),
PageSize: int(req.PageSize),
}
@ -489,6 +501,7 @@ func (b *BundleProvider) GetTaskWorkLogList(_ context.Context, req *bundle.TaskW
OperationTime: int64(record.OperationTime),
CreatedAt: int64(record.CreatedAt),
UpdatedAt: int64(record.UpdatedAt),
BundleTaskType: bundle.BundleTaskType(record.BundleTaskType),
})
}
@ -517,6 +530,7 @@ func (b *BundleProvider) CreateTaskWorkLog(_ context.Context, req *bundle.Create
Remark: req.Remark,
OperatorName: req.OperatorName,
OperatorNum: req.OperatorNum,
BundleTaskType: int(req.BundleTaskType),
}
// 调用logic层

View File

@ -23,6 +23,7 @@ func CreateBundle(req *model.BundleProfile) (res *bundle.CommonResponse, err err
return
}
// 测试注释
func UpdateBundle(req *model.BundleProfile) (res *bundle.CommonResponse, err error) {
res = new(bundle.CommonResponse)
err = app.ModuleClients.BundleDB.Model(&model.BundleProfile{}).Where("uuid = ? and language= ?", req.UUID, req.Language).Updates(req).Error
@ -64,6 +65,10 @@ func BundleList(req *bundle.BundleListRequest) (res *bundle.BundleListResponse,
query = query.Where("language like ?", req.Language)
}
if req.BundleType != 0 {
query = query.Where("bundle_type = ?", req.BundleType)
}
count := *query
if req.PageSize != 0 && req.Page != 0 {
@ -88,6 +93,8 @@ func BundleList(req *bundle.BundleListRequest) (res *bundle.BundleListResponse,
CompanySign: bundleProfile.CompanySign,
ContractDuration: int64(bundleProfile.ContractDuration),
BundleCommonUid: bundleProfile.BundleCommonUid,
BundleType: int64(bundleProfile.BundleType),
PayLaterTime: int64(bundleProfile.PayLaterTime),
})
}
@ -127,6 +134,8 @@ func BundleDetail(uuid string) (res *bundle.BundleProfile, err error) {
CompanySign: bundleProfile.CompanySign,
ContractDuration: int64(bundleProfile.ContractDuration),
BundleCommonUid: bundleProfile.BundleCommonUid,
BundleType: int64(bundleProfile.BundleType),
PayLaterTime: int64(bundleProfile.PayLaterTime),
}
return
}
@ -150,6 +159,10 @@ func BundleListV2(req *bundle.BundleListRequest) (res *bundle.BundleListResponse
baseQuery = baseQuery.Where("language like ?", req.Language)
}
if req.BundleType != 0 {
baseQuery = baseQuery.Where("bundle_type = ?", req.BundleType)
}
var total int64
if err = baseQuery.Count(&total).Error; err != nil {
return res, commonErr.ReturnError(err, msg.ErrorGetBundleList, "获取套餐总数失败: ")
@ -224,6 +237,8 @@ func BundleListV2(req *bundle.BundleListRequest) (res *bundle.BundleListResponse
BundleProfileLang: bundleProfileLang,
ShelfStatus: int64(bundleProfile.ShelfStatus),
FontColor: bundleProfile.FontColor,
BundleType: int64(bundleProfile.BundleType),
PayLaterTime: int64(bundleProfile.PayLaterTime),
})
}
}
@ -378,7 +393,7 @@ func BundleListH5V2(req *bundle.BundleListRequest) (res *bundle.BundleListRespon
res.Bundles = make([]*bundle.BundleProfile, 0)
bundles := make([]*model.BundleProfile, 0)
query := app.ModuleClients.BundleDB.Model(&model.BundleProfile{}).Where("shelf_status = ?", 1).Preload("BundleProfileLang")
query := app.ModuleClients.BundleDB.Model(&model.BundleProfile{}).Where("shelf_status = ?", model.BundleShelfStatusUp).Preload("BundleProfileLang")
//query = query.Where("shelf_status = ?", 1) //上架的
if req.Name != "" {
@ -393,6 +408,10 @@ func BundleListH5V2(req *bundle.BundleListRequest) (res *bundle.BundleListRespon
query = query.Where("language like ?", req.Language)
}
if req.BundleType != 0 {
query = query.Where("bundle_type = ?", req.BundleType)
}
count := *query
// 排序sort 升序,相同 sort 按 created_at 倒序
@ -451,6 +470,8 @@ func BundleListH5V2(req *bundle.BundleListRequest) (res *bundle.BundleListRespon
BundleProfileLang: bundleProfileLang,
ShelfStatus: int64(bundleProfile.ShelfStatus),
FontColor: bundleProfile.FontColor,
BundleType: int64(bundleProfile.BundleType),
PayLaterTime: int64(bundleProfile.PayLaterTime),
})
}
}

View File

@ -174,10 +174,23 @@ func GetBundleBalanceByUserId(req *bundle.GetBundleBalanceByUserIdReq) (data mod
Where("u.id = ?", req.UserId).
Where("bb.month = ?", time.Now().Format("2006-01")).
Order("bor.expiration_time desc")
if req.OrderUUID != "" {
session = session.Where("bb.order_uuid = ?", req.OrderUUID)
}
err = session.First(&data).Error
return
}
func GetBundleOrderListByUserId(userId int32) (orderList []string, err error) {
currentMonth := time.Now().Format("2006-01")
timeNow := time.Now().Format("2006-01-02 15:04:05")
err = app.ModuleClients.BundleDB.Table("bundle_balance").Where("user_id = ? and expired_at >= ?", userId, timeNow).Where("month = ?", currentMonth).Where("deleted_at is null").Select("order_uuid").Find(&orderList).Error
if err != nil {
return nil, err
}
return orderList, nil
}
func GetBundleBalanceByOrderUuid(orderUuid string) (data model.BundleBalance, err error) {
err = app.ModuleClients.BundleDB.Model(&model.BundleBalance{}).Where("order_uuid = ?", orderUuid).First(&data).Error
if err != nil {

View File

@ -0,0 +1,123 @@
package dao
import (
"micro-bundle/internal/model"
"micro-bundle/pkg/app"
"time"
)
// ExistsOverduePayLaterBundle 是否存在「套餐 + 先用后付 + 到期未付」订单
// 命中即应拦截后续套餐下单(增值仍允许「无限续杯」)。
func ExistsOverduePayLaterBundle(customerID string) (bool, error) {
if customerID == "" {
return false, nil
}
var cnt int64
err := app.ModuleClients.BundleDB.
Model(&model.BundleOrderRecords{}).
Where("customer_id = ? AND order_mode = ? AND (status = ? OR pay_later_status = ?)",
customerID,
model.OrderModePayLater,
model.BundleStatusOverdue,
model.PayLaterStatusOverdue,
).Count(&cnt).Error
if err != nil {
return false, err
}
return cnt > 0, nil
}
// ExistsPendingPayLaterBundle 是否存在「套餐 + 先用后付 + 待付款(未逾期)」订单。
// 命中即提示用户去支付,避免同时存在多个先用后付未支付套餐订单。
func ExistsPendingPayLaterBundle(customerID string) (bool, error) {
if customerID == "" {
return false, nil
}
var cnt int64
err := app.ModuleClients.BundleDB.
Model(&model.BundleOrderRecords{}).
Where("customer_id = ? AND order_mode = ? AND pay_later_status = ? AND status = ?",
customerID,
model.OrderModePayLater,
model.PayLaterStatusPending,
model.BundleStatusSignedUnpaid,
).Count(&cnt).Error
if err != nil {
return false, err
}
return cnt > 0, nil
}
// ExistsActiveBundle 是否存在有效期内的套餐订单(已签未付 / 已签已付)
// 用于保证:套餐有效期内只存在一笔订单。
func ExistsActiveBundle(customerID string) (bool, error) {
if customerID == "" {
return false, nil
}
now := time.Now().Format("2006-01-02 15:04:05")
var cnt int64
err := app.ModuleClients.BundleDB.
Model(&model.BundleOrderRecords{}).
Where("customer_id = ? AND status IN (?,?) AND (expiration_time = '' OR expiration_time IS NULL OR expiration_time >= ?)",
customerID,
model.BundleStatusSignedUnpaid,
model.BundleStatusPaid,
now,
).Count(&cnt).Error
if err != nil {
return false, err
}
return cnt > 0, nil
}
// MarkBundleOverdue 套餐先用后付到期扫描:将到期且未付的订单置为 Overdue
func MarkBundleOverdue(now time.Time) (int64, error) {
t := now.Format("2006-01-02 15:04:05")
r := app.ModuleClients.BundleDB.
Model(&model.BundleOrderRecords{}).
Where("order_mode = ? AND pay_later_status = ? AND due_time <> '' AND due_time <= ?",
model.OrderModePayLater, model.PayLaterStatusPending, t).
Updates(map[string]interface{}{
"pay_later_status": model.PayLaterStatusOverdue,
"status": model.BundleStatusOverdue,
})
return r.RowsAffected, r.Error
}
// MarkValueAddExpiredByDue 增值服务到期扫描:到期未付 → pay_later_status=逾期未付。
// 注意:余量是否作废由余量模块依据 pay_later_status 判定,订单侧不写入余量字段(is_expired)。
func MarkValueAddExpiredByDue(now time.Time) (int64, error) {
t := now.Format("2006-01-02 15:04:05")
r := app.ModuleClients.BundleDB.
Model(&model.BundleOrderValueAdd{}).
Where("order_mode = ? AND pay_later_status = ? AND due_time <> '' AND due_time <= ?",
model.OrderModePayLater, model.PayLaterStatusPending, t).
Updates(map[string]interface{}{
"pay_later_status": model.PayLaterStatusOverdue,
})
return r.RowsAffected, r.Error
}
// IsValueAddRecordUsable 判断增值订单余量是否可用
// 规则IsExpired=true 直接不可用;先用后付 + 到期未付 直接不可用。
func IsValueAddRecordUsable(rec *model.BundleOrderValueAdd) bool {
if rec == nil {
return false
}
if rec.IsExpired {
return false
}
if rec.OrderMode == model.OrderModePayLater {
if rec.PayLaterStatus == model.PayLaterStatusOverdue {
return false
}
if rec.PayLaterStatus == model.PayLaterStatusPending && rec.DueTime != "" {
if t, err := time.ParseInLocation("2006-01-02 15:04:05", rec.DueTime, time.Local); err == nil {
if time.Now().After(t) {
return false
}
}
}
}
return true
}

View File

@ -115,6 +115,10 @@ func UpdateOrderRecordByOrderNO(orderRecord *model.BundleOrderRecords) (res *bun
valueAdd.CheckoutSessionId = orderRecord.CheckoutSessionId
valueAdd.CheckoutSessionUrl = orderRecord.CheckoutSessionUrl
}
// 先用后付:主订单支付成功 → 子订单先用后付状态置为已付
if orderRecord.Status == 2 {
valueAdd.PayLaterStatus = model.PayLaterStatusPaid
}
if orderRecord.Status == 2 {
tempValues := make([]*model.BundleOrderValueAdd, 0)
@ -148,6 +152,10 @@ func UpdateOrderRecordByOrderNO(orderRecord *model.BundleOrderRecords) (res *bun
result := feeRounded.Add(addition)
valueAdd.HandlingFee = result.String()
}
// 主订单若是先用后付,同步 pay_later_status=已付(若处于逾期未付,也复位)
if tempRecord.OrderMode == model.OrderModePayLater {
orderRecord.PayLaterStatus = model.PayLaterStatusPaid
}
}
@ -235,6 +243,14 @@ func OrderRecordsList(req *bundle.OrderRecordsRequest) (res *bundle.OrderRecords
query = query.Where("`micro-account`.`user`.`tel_num` like ?", "%"+req.TelNum+"%")
}
if req.OrderMode != 0 {
query = query.Where("`bundle_order_records`.order_mode = ?", req.OrderMode)
}
if req.PayLaterStatus != 0 {
query = query.Where("`bundle_order_records`.pay_later_status = ?", req.PayLaterStatus)
}
count := *query
if req.PageSize != 0 && req.Page != 0 {
@ -282,6 +298,10 @@ func OrderRecordsList(req *bundle.OrderRecordsRequest) (res *bundle.OrderRecords
ExpirationTime: record.ExpirationTime,
PurchaseType: record.PurchaseType,
RenewalOrderUUID: record.RenewalOrderUUID,
OrderMode: record.OrderMode,
DueTime: record.DueTime,
PayLaterStatus: record.PayLaterStatus,
ContractTplType: record.ContractTplType,
})
}
@ -387,6 +407,10 @@ func OrderRecordDetail(req *bundle.OrderRecordsDetailRequest) (res *bundle.Order
ReSignature: int32(orderRecord.ReSignature),
PurchaseType: orderRecord.PurchaseType,
RenewalOrderUUID: orderRecord.RenewalOrderUUID,
OrderMode: orderRecord.OrderMode,
DueTime: orderRecord.DueTime,
PayLaterStatus: orderRecord.PayLaterStatus,
ContractTplType: orderRecord.ContractTplType,
}
res.AddInfos = make([]*bundle.AddInfo, 0)
res.AddInfos = addInfos
@ -432,27 +456,44 @@ func CreateOrderAddRecord(req *bundle.OrderAddRecord) (res *bundle.CommonRespons
}
}()
orderNo := utils.GetOrderNo()
// 增值订单默认先用后付(规则 2增值服务订单只有先用后付类型
addOrderMode := req.OrderMode
if addOrderMode == 0 {
addOrderMode = model.OrderModePayLater
}
addPayLaterStatus := req.PayLaterStatus
if addOrderMode == model.OrderModePayLater && addPayLaterStatus == 0 {
addPayLaterStatus = model.PayLaterStatusPending
}
addContractTplType := req.ContractTplType
if addOrderMode == model.OrderModePayLater && addContractTplType == 0 {
addContractTplType = model.ContractTplValueAddPayLater
}
var childOrders []*model.BundleOrderValueAdd
for _, i := range req.AddPriceOptionsList {
childOrder := &model.BundleOrderValueAdd{
UUID: app.ModuleClients.SfNode.Generate().Base64(),
OrderUUID: req.OrderUUID, // 修正: 这里应使用主订单UUID
CustomerID: req.CustomerID,
CustomerNum: req.CustomerNum,
CustomerName: req.CustomerName,
ServiceType: i.ServiceType,
CurrencyType: i.CurrencyType,
Amount: float64(i.Amount),
OrderNo: orderNo,
Num: i.Num,
Unit: i.Unit,
ValueAddUUID: i.ValueUid,
Source: 2,
PaymentStatus: 1,
SignContract: req.SignContract,
Signature: req.Signature,
SignedTime: req.SignedTime,
Snapshot: req.Snapshot,
UUID: app.ModuleClients.SfNode.Generate().Base64(),
OrderUUID: req.OrderUUID, // 修正: 这里应使用主订单UUID
CustomerID: req.CustomerID,
CustomerNum: req.CustomerNum,
CustomerName: req.CustomerName,
ServiceType: i.ServiceType,
CurrencyType: i.CurrencyType,
Amount: float64(i.Amount),
OrderNo: orderNo,
Num: i.Num,
Unit: i.Unit,
ValueAddUUID: i.ValueUid,
Source: 2,
PaymentStatus: 1,
SignContract: req.SignContract,
Signature: req.Signature,
SignedTime: req.SignedTime,
Snapshot: req.Snapshot,
OrderMode: addOrderMode,
DueTime: req.DueTime,
PayLaterStatus: addPayLaterStatus,
ContractTplType: addContractTplType,
}
childOrders = append(childOrders, childOrder)
@ -522,6 +563,12 @@ func OrderRecordsListV2(req *bundle.OrderRecordsRequestV2) (res *bundle.OrderRec
if req.BundlePayStart != "" && req.BundlePayEnd != "" {
modelObj = modelObj.Where("bundle_order_records.pay_time between ? and ?", req.BundlePayStart, req.BundlePayEnd)
}
if req.OrderMode != 0 {
modelObj = modelObj.Where("bundle_order_records.order_mode = ?", req.OrderMode)
}
if req.PayLaterStatus != 0 {
modelObj = modelObj.Where("bundle_order_records.pay_later_status = ?", req.PayLaterStatus)
}
err = modelObj.Count(&count).Error
if req.PageSize != 0 && req.Page != 0 {
modelObj = modelObj.Limit(int(req.PageSize)).Offset(int(req.Page-1) * int(req.PageSize))
@ -551,6 +598,10 @@ func OrderRecordsListV2(req *bundle.OrderRecordsRequestV2) (res *bundle.OrderRec
InviterId: record.InviterID,
PurchaseType: record.PurchaseType,
RenewalOrderUUID: record.RenewalOrderUUID,
OrderMode: record.OrderMode,
DueTime: record.DueTime,
PayLaterStatus: record.PayLaterStatus,
ContractTplType: record.ContractTplType,
}
// 聚合子订单
@ -582,6 +633,10 @@ func OrderRecordsListV2(req *bundle.OrderRecordsRequestV2) (res *bundle.OrderRec
CheckoutSessionId: sub.CheckoutSessionId,
CustomerID: sub.CustomerID,
VideoNum: videoNum,
OrderMode: sub.OrderMode,
DueTime: sub.DueTime,
PayLaterStatus: sub.PayLaterStatus,
ContractTplType: sub.ContractTplType,
}
}
}
@ -1031,6 +1086,10 @@ func UpdateOrderRecordByOrderUuid(orderRecord *model.BundleOrderRecords) (res *b
valueAdd.CheckoutSessionId = orderRecord.CheckoutSessionId
valueAdd.CheckoutSessionUrl = orderRecord.CheckoutSessionUrl
}
// 先用后付:主订单支付成功 → 子订单先用后付状态置为已付
if orderRecord.Status == 2 {
valueAdd.PayLaterStatus = model.PayLaterStatusPaid
}
if orderRecord.Status == 2 {
tempValues := make([]*model.BundleOrderValueAdd, 0)
@ -1064,6 +1123,10 @@ func UpdateOrderRecordByOrderUuid(orderRecord *model.BundleOrderRecords) (res *b
result := feeRounded.Add(addition)
valueAdd.HandlingFee = result.String()
}
// 主订单若是先用后付,同步 pay_later_status=已付(若处于逾期未付,也复位)
if tempRecord.OrderMode == model.OrderModePayLater {
orderRecord.PayLaterStatus = model.PayLaterStatusPaid
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
package dao
import (
"errors"
"regexp"
"strings"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"micro-bundle/internal/model"
"micro-bundle/internal/testutil"
)
// ---- GetPendingTaskLayout ----
// sqlmock + gorm 配合:QueryMatcherEqual 要求 SQL 字面量精确匹配;但
// gorm 生成的字段顺序与时间字段处理可能因版本略有差异,这里用 Regexp 匹配
// 关键子串,避免对生成 SQL 的细节过度耦合。
//
// DAO 测试不用 t.Parallel():SetupTaskBenchDB 会改写全局 app.ModuleClients,
// 并行执行会导致 sqlmock 期望链互相污染。
var reSelectLayout = regexp.MustCompile(`SELECT \* FROM .task_pending_layout. WHERE id = \?`)
var reInsertLayout = regexp.MustCompile(`INSERT INTO .task_pending_layout. .*\)\s*VALUES \(.*\)\s*ON DUPLICATE KEY UPDATE .data.`)
var reInsertHidden = regexp.MustCompile(`INSERT INTO .task_assignee_hidden.`)
func TestGetPendingTaskLayout_NotFound(t *testing.T) {
mock := testutil.SetupTaskBenchDB(t)
mock.MatchExpectationsInOrder(false)
mock.ExpectQuery(reSelectLayout.String()).
WithArgs(1).
WillReturnError(gormNotFound())
got, err := GetPendingTaskLayout()
require.Error(t, err)
assert.Empty(t, got)
}
func TestGetPendingTaskLayout_Success(t *testing.T) {
mock := testutil.SetupTaskBenchDB(t)
mock.MatchExpectationsInOrder(false)
rows := sqlmock.NewRows([]string{"id", "data"}).AddRow(1, `[{"fieldKey":"subNum"}]`)
// gorm 的 First() 内部会带一个 LIMIT 参数,这里用 .* 兼容。
mock.ExpectQuery(reSelectLayout.String()).
WillReturnRows(rows)
got, err := GetPendingTaskLayout()
require.NoError(t, err)
assert.JSONEq(t, `[{"fieldKey":"subNum"}]`, got)
}
// ---- SetPendingTaskLayout ----
func TestSetPendingTaskLayout_Success(t *testing.T) {
mock := testutil.SetupTaskBenchDB(t)
mock.MatchExpectationsInOrder(false)
// gorm 会在 Create + OnConflict 时开事务,显式声明 Begin/Commit。
mock.ExpectBegin()
mock.ExpectExec(reInsertLayout.String()).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()
err := SetPendingTaskLayout(`[{"fieldKey":"subNum"}]`)
require.NoError(t, err)
}
// ---- AddHiddenTaskAssignee ----
func TestAddHiddenTaskAssignee_Success(t *testing.T) {
mock := testutil.SetupTaskBenchDB(t)
mock.MatchExpectationsInOrder(false)
mock.ExpectBegin()
mock.ExpectExec(reInsertHidden.String()).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()
err := AddHiddenTaskAssignee("员工A", "E001")
require.NoError(t, err)
}
// ---- helpers ----
// gormNotFound 返回 gorm.ErrRecordNotFound。
// gorm 内部 First()/Find() 命中零行时返回该错误。
func gormNotFound() error {
return gorm.ErrRecordNotFound
}
// 保留 errors 包,留给后续事务测试使用 dialError 等
var _ = errors.New
// ---- taskStatsQueryOptions ----
// 工具函数:把字符串压成"压缩"形式,便于断言不依赖空白。
// 例如把多行 raw-string 拼成一行,去掉首尾空白,折叠中间空白。
func compactSQL(s string) string {
// 把任意空白序列替换成单个空格,再去掉首尾。
re := regexp.MustCompile(`\s+`)
return strings.TrimSpace(re.ReplaceAllString(s, " "))
}
// TestTaskStatsQueryOptions_PayLater 验证先用后付分支的过滤条件。
func TestTaskStatsQueryOptions_PayLater(t *testing.T) {
taskType, billingType, orderFilter, latestOrderFilter := taskStatsQueryOptions(model.BundleTaskTypePayLater)
assert.Equal(t, model.BundleTaskTypePayLater, taskType)
assert.Equal(t, model.BillingTypeBNPL, billingType)
assert.Equal(t, "", latestOrderFilter, "pay-later 分支不应启用 NOT EXISTS 去重")
compact := compactSQL(orderFilter)
// 必备条件
// pay-later 分支对应增值先用后付,OrderType 必须是增值服务订单 (order_type=2)
assert.Contains(t, compact, "bor.order_type = 2")
assert.Contains(t, compact, "bor.contract_tpl_type = 3")
assert.Contains(t, compact, "bor.pay_later_status IN (1, 2)")
assert.Contains(t, compact, "bor.status IN (1, 2, 4)")
// 不应再依赖 order_mode
assert.NotContains(t, compact, "order_mode")
}
// TestTaskStatsQueryOptions_Normal 验证普通任务分支的过滤条件。
// 该分支由两个互斥 case 组成:
// - Case A: 套餐-普通 (order_type=1, contract_tpl_type=1, status=2)
// - Case B: 套餐先用后付的另一种存法 (order_type=2, contract_tpl_type=2,
// status IN (1,2,4) AND pay_later_status IN (1,2))
func TestTaskStatsQueryOptions_Normal(t *testing.T) {
taskType, billingType, orderFilter, latestOrderFilter := taskStatsQueryOptions(model.BundleTaskTypeNormal)
assert.Equal(t, model.BundleTaskTypeNormal, taskType)
assert.Equal(t, model.BillingTypeNormal, billingType)
compact := compactSQL(orderFilter)
// Case A: 套餐-普通,仅已签已支付
assert.Contains(t, compact, "bor.order_type = 1")
assert.Contains(t, compact, "bor.contract_tpl_type = 1")
assert.Contains(t, compact, "bor.status = 2")
// Case B: 套餐先用后付的另一种存法
assert.Contains(t, compact, "bor.order_type = 2")
assert.Contains(t, compact, "bor.contract_tpl_type = 2")
assert.Contains(t, compact, "bor.status IN (1, 2, 4)")
assert.Contains(t, compact, "bor.pay_later_status IN (1, 2)")
// 不应再依赖 order_mode(回归保护)
assert.NotContains(t, compact, "order_mode")
// NOT EXISTS 内层必须与外层对齐,使用 bor2 别名
compactLatest := compactSQL(latestOrderFilter)
// Case A 在内层的镜像
assert.Contains(t, compactLatest, "bor2.order_type = 1")
assert.Contains(t, compactLatest, "bor2.contract_tpl_type = 1")
assert.Contains(t, compactLatest, "bor2.status = 2")
// Case B 在内层的镜像
assert.Contains(t, compactLatest, "bor2.order_type = 2")
assert.Contains(t, compactLatest, "bor2.contract_tpl_type = 2")
assert.Contains(t, compactLatest, "bor2.status IN (1, 2, 4)")
assert.Contains(t, compactLatest, "bor2.pay_later_status IN (1, 2)")
// 平局规则保持不变
assert.Contains(t, compactLatest, "(bor2.created_at > bor.created_at OR (bor2.created_at = bor.created_at AND bor2.id > bor.id))")
}
// TestTaskStatsQueryOptions_DefaultToNormal 非 pay-later 任意值都走普通任务分支。
func TestTaskStatsQueryOptions_DefaultToNormal(t *testing.T) {
for _, v := range []int{0, 1, 99, -1} {
taskType, billingType, orderFilter, _ := taskStatsQueryOptions(v)
assert.Equal(t, model.BundleTaskTypeNormal, taskType, "input=%d 应走 normal 分支", v)
assert.Equal(t, model.BillingTypeNormal, billingType, "input=%d 计费类型应为 Normal", v)
assert.Contains(t, compactSQL(orderFilter), "bor.order_type = 1")
assert.NotContains(t, compactSQL(orderFilter), "order_mode")
}
}

View File

@ -11,6 +11,15 @@ type TaskQueryRequest struct {
SortBy string `json:"sortBy"` // 排序字段(支持白名单字段)
SortType string `json:"sortType"` // 排序类型 asc/desc
SubNums []string `json:"subNums"` // 选中导出的艺人编号集合(可选)
BundleTaskType int `json:"bundleTaskType"` // 任务所属套餐类型 0/1:基础套餐 2:先用后付套餐
}
// PendingAssignQueryRequest 查询艺人可指派数量请求参数
type PendingAssignQueryRequest struct {
SubNums []string `json:"subNums"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
BundleTaskType int `json:"bundleTaskType"` // 任务所属套餐类型 0/1:基础套餐 2:先用后付套餐
}
// TaskAssignRequest 指派任务请求参数
@ -29,6 +38,7 @@ type TaskAssignRequest struct {
AssignReportCount int `json:"assignReportCount"` // 指派竞品报告数
TaskBatch string `json:"taskBatch"` // 任务批次
UserID int `json:"userID"` // 用户ID
BundleTaskType int `json:"bundleTaskType"` // 任务所属套餐类型 1:基础套餐 2:先用后付套餐
}
// BatchAssignItem 批量指派项(仅写入指派记录,不更新任务管理表)
@ -47,6 +57,7 @@ type BatchAssignItem struct {
AssignReportCount int `json:"assignReportCount"` // 指派竞品报告数
TaskBatch string `json:"taskBatch"` // 任务批次
UserID int `json:"userID"` // 用户ID
BundleTaskType int `json:"bundleTaskType"` // 任务所属套餐类型 0/1:基础套餐 2:先用后付套餐
}
// EmployeeTaskQueryRequest 员工任务查询请求参数
@ -61,6 +72,7 @@ type EmployeeTaskQueryRequest struct {
EndCompleteTime string `json:"endCompleteTime"` // 结束完成时间
Status int `json:"status"` // 反馈完成状态
TaskBatch string `json:"taskBatch"` // 任务批次
BundleTaskType int `json:"bundleTaskType"` // 任务所属套餐类型 0:全部 1:基础套餐 2:先用后付套餐
Page int `json:"page"` // 页码
PageSize int `json:"pageSize"` // 每页数量
@ -91,6 +103,8 @@ type TaskAssignRecordsQueryRequest struct {
PageSize int `json:"pageSize"` // 每页数量
SortBy string `json:"sortBy"` // 排序字段(白名单)
SortType string `json:"sortType"` // 排序方式
BundleTaskType int `json:"bundleTaskType"` // 任务所属套餐类型 0:全部 1:基础套餐 2:先用后付套餐
}
// 任务记录表返回结构体
@ -120,6 +134,7 @@ type TaskAssignRecordsResponse struct {
CompletePostCount int `gorm:"column:complete_post_count;comment:已完成图文数" json:"completePostCount"`
CompleteDataCount int `gorm:"column:complete_data_count;comment:已完成数据数" json:"completeDataCount"`
CompleteReportCount int `gorm:"column:complete_report_count;comment:已完成竞品报告数" json:"completeReportCount"`
BundleTaskType int `gorm:"column:bundle_task_type;comment:任务所属套餐类型 1:基础套餐 2:先用后付套餐" json:"bundleTaskType"`
UpdatedAt time.Time `gorm:"column:updated_at;comment:更新时间" json:"updatedAt"`
}
@ -245,6 +260,8 @@ type CreateTaskWorkLogRequest struct {
// 操作人信息
OperatorName string `json:"operatorName"` // 任务操作人姓名
OperatorNum string `json:"operatorNum"` // 任务操作人账号
BundleTaskType int `json:"bundleTaskType"` // 任务所属套餐类型 0:默认 1:基础套餐 2:先用后付套餐
}
type TaskWorkLogQueryRequest struct {
@ -254,6 +271,7 @@ type TaskWorkLogQueryRequest struct {
ArtistUUID string `json:"artistUUID"`
SubNum string `json:"subNum"`
ArtistName string `json:"artistName"`
BundleTaskType int `json:"bundleTaskType"` // 任务所属套餐类型 0:全部 1:基础套餐 2:先用后付套餐
Page int `json:"page"`
PageSize int `json:"pageSize"`
}

View File

@ -262,6 +262,19 @@ func GetBundleBalanceByUserId(req *bundle.GetBundleBalanceByUserIdReq) (*bundle.
return result, nil
}
func GetBundleOrderListByUserId(req *bundle.GetBundleOrderListByUserIdReq) (*bundle.GetBundleOrderListByUserIdResp, error) {
orderList, err := dao.GetBundleOrderListByUserId(req.UserId)
if err != nil {
logger.Error(err)
return nil, errors.New("查询订单列表失败")
}
resp := &bundle.GetBundleOrderListByUserIdResp{}
resp.OrderUuid = lo.Map(orderList, func(m string, _ int) string {
return m
})
return resp, nil
}
// 获取最初非续费套餐订单的开始时间
// 查询失败返回2 和 默认事件
func GetBundleBalanceByOrderUUID(orderUUID string) (time.Time, int, error) {
@ -286,6 +299,7 @@ func GetBundleBalanceByOrderUUID(orderUUID string) (time.Time, int, error) {
func AddBundleBalance(req *bundle.AddBundleBalanceReq) (*bundle.AddBundleBalanceResp, error) {
data := model.BundleBalanceUsePo{
UserId: int(req.UserId),
OrderUUID: req.OrderUUID,
AccountNumber: int(req.AccountConsumptionNumber),
ImageNumber: int(req.ImageConsumptionNumber),
VideoNumber: int(req.VideoConsumptionNumber),

View File

@ -68,17 +68,19 @@ func SaveBundle(req *bundle.BundleProfile) (res *bundle.SaveResponse, err error)
}
//套餐主表数据
bundleProfile := &model.BundleProfile{
Name: req.Name,
Sort: req.Sort,
Content: req.Content,
Price: req.Price,
PriceType: req.PriceType,
Contract: "https://e-cdn.fontree.cn/fonchain-main/prod/file/contract/saas/template-25122501.pdf",
ImgOption: int8(req.ImgOption),
BgImg1: req.BgImg1,
BgImg2: req.BgImg2,
ShelfStatus: 2, //默认初始状态为2-下架
FontColor: req.FontColor,
Name: req.Name,
Sort: req.Sort,
Content: req.Content,
Price: req.Price,
PriceType: req.PriceType,
Contract: "https://e-cdn.fontree.cn/fonchain-main/prod/file/contract/saas/template-25122501.pdf",
ImgOption: int8(req.ImgOption),
BgImg1: req.BgImg1,
BgImg2: req.BgImg2,
ShelfStatus: model.BundleShelfStatusDown, //默认初始状态为2-下架
FontColor: req.FontColor,
BundleType: req.BundleType,
PayLaterTime: req.PayLaterTime,
}
bundleLang := &model.BundleProfileLang{
Name: req.Name,
@ -202,6 +204,8 @@ func SaveBundle(req *bundle.BundleProfile) (res *bundle.SaveResponse, err error)
serviceName = "账号数"
case msg.AvailableTimeService:
serviceName = "可用时长"
case msg.CompetitiveService:
serviceName = "竞品数"
}
return res, fmt.Errorf("服务类型%s的额度不兼容套餐权益额度为%d附加权益额度为%d",
serviceName, benefitsQuota.QuotaValue, optionalQuota.QuotaValue)
@ -288,15 +292,17 @@ func SaveBundle(req *bundle.BundleProfile) (res *bundle.SaveResponse, err error)
}
if req.Language == msg.ZH_CN {
updateBundle := map[string]interface{}{
"name": req.Name,
"sort": req.Sort,
"content": req.Content,
"price": req.Price,
"price_type": req.PriceType,
"img_option": req.ImgOption,
"bg_img1": req.BgImg1,
"bg_img2": req.BgImg2,
"font_color": req.FontColor,
"name": req.Name,
"sort": req.Sort,
"content": req.Content,
"price": req.Price,
"price_type": req.PriceType,
"img_option": req.ImgOption,
"bg_img1": req.BgImg1,
"bg_img2": req.BgImg2,
"font_color": req.FontColor,
"bundle_type": req.BundleType,
"pay_later_time": req.PayLaterTime,
}
if err = dao.TxUpdateBundle(tx, req.Uuid, updateBundle); err != nil {
return res, errors.New("更新套餐信息失败")
@ -333,6 +339,7 @@ func BundleListV2(req *bundle.BundleListRequest) (res *bundle.BundleListResponse
func BundleDetailV2(req *bundle.BundleDetailRequest) (res *bundle.BundleDetailResponseV2, err error) {
res = new(bundle.BundleDetailResponseV2)
bundleProfile := &bundle.BundleProfile{}
var bundleProfileLang *bundle.BundleProfileLang
if req.Uuid == "" {
return res, errors.New("缺少套餐UUID")
@ -341,22 +348,55 @@ func BundleDetailV2(req *bundle.BundleDetailRequest) (res *bundle.BundleDetailRe
if err != nil {
return res, errors.New("获取套餐信息失败")
}
if req.Language != "" {
for _, lang := range detail.BundleProfileLang {
if lang.Language == req.Language {
bundleProfileLang = &bundle.BundleProfileLang{
Uuid: lang.UUID,
Name: lang.Name,
Price: lang.Price,
PriceType: lang.PriceType,
Content: lang.Content,
PayLaterTime: lang.PayLaterTime,
}
break
}
}
} else {
//默认返回中文数据
for _, lang := range detail.BundleProfileLang {
if lang.Language == msg.ZH_CN {
bundleProfileLang = &bundle.BundleProfileLang{
Uuid: lang.UUID,
Name: lang.Name,
Price: lang.Price,
PriceType: lang.PriceType,
Content: lang.Content,
PayLaterTime: lang.PayLaterTime,
}
break
}
}
}
if detail != nil {
bundleProfile = &bundle.BundleProfile{
Uuid: detail.UUID,
Name: detail.Name,
Content: detail.Content,
Price: detail.Price,
PriceType: detail.PriceType,
ImgOption: int32(detail.ImgOption),
BgImg1: detail.BgImg1,
BgImg2: detail.BgImg2,
FontColor: detail.FontColor,
Sort: detail.Sort,
ShelfStatus: detail.ShelfStatus,
CreatedAt: detail.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: detail.UpdatedAt.Format("2006-01-02 15:04:05"),
Contract: detail.Contract,
Uuid: detail.UUID,
Name: bundleProfileLang.Name,
Content: bundleProfileLang.Content,
Price: bundleProfileLang.Price,
PriceType: bundleProfileLang.PriceType,
ImgOption: int32(detail.ImgOption),
BgImg1: detail.BgImg1,
BgImg2: detail.BgImg2,
Language: req.Language,
FontColor: detail.FontColor,
Sort: detail.Sort,
ShelfStatus: detail.ShelfStatus,
CreatedAt: detail.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: detail.UpdatedAt.Format("2006-01-02 15:04:05"),
Contract: detail.Contract,
BundleType: int64(detail.BundleType),
PayLaterTime: int64(bundleProfileLang.PayLaterTime),
}
if len(detail.BundleToValueAddService) > 0 {
var valueUuids []string
@ -421,20 +461,22 @@ func BundleDetailV2(req *bundle.BundleDetailRequest) (res *bundle.BundleDetailRe
bundleProfileLangs := make([]*bundle.BundleProfileLang, 0, len(detail.BundleProfileLang))
for _, lang := range detail.BundleProfileLang {
bundleProfileLangs = append(bundleProfileLangs, &bundle.BundleProfileLang{
Uuid: lang.UUID,
Name: lang.Name,
Price: lang.Price,
PriceType: lang.PriceType,
Content: lang.Content,
Language: lang.Language,
ImgOption: int32(detail.ImgOption),
BgImg1: detail.BgImg1,
BgImg2: detail.BgImg2,
FontColor: detail.FontColor,
Sort: detail.Sort,
ShelfStatus: detail.ShelfStatus,
CreatedAt: time.Unix(lang.CreatedAt, 0).Format("2006-01-02 15:04:05"),
UpdatedAt: time.Unix(int64(lang.UpdatedAt), 0).Format("2006-01-02 15:04:05"),
Uuid: lang.UUID,
Name: lang.Name,
Price: lang.Price,
PriceType: lang.PriceType,
Content: lang.Content,
Language: lang.Language,
PayLaterTime: int64(lang.PayLaterTime),
ImgOption: int32(detail.ImgOption),
BgImg1: detail.BgImg1,
BgImg2: detail.BgImg2,
FontColor: detail.FontColor,
Sort: detail.Sort,
ShelfStatus: detail.ShelfStatus,
BundleType: int64(detail.BundleType),
CreatedAt: time.Unix(lang.CreatedAt, 0).Format("2006-01-02 15:04:05"),
UpdatedAt: time.Unix(int64(lang.UpdatedAt), 0).Format("2006-01-02 15:04:05"),
})
}
bundleProfile.BundleProfileLang = bundleProfileLangs
@ -469,6 +511,8 @@ func BundleLangDetailV2(req *bundle.BundleDetailRequest) (res *bundle.BundleProf
res.Sort = detail.Sort
res.ShelfStatus = detail.ShelfStatus
res.Contract = detail.Contract
res.BundleType = int64(detail.BundleType)
res.PayLaterTime = int64(detail.PayLaterTime)
if len(detail.BundleToValueAddService) > 0 {
var serviceLangInfos []*bundle.ServiceLangInfo
for _, service := range detail.BundleToValueAddService {
@ -486,6 +530,7 @@ func BundleLangDetailV2(req *bundle.BundleDetailRequest) (res *bundle.BundleProf
res.PriceType = lang.PriceType
res.Content = lang.Content
res.Language = lang.Language
res.PayLaterTime = int64(lang.PayLaterTime)
res.CreatedAt = time.Unix(lang.CreatedAt, 0).Format("2006-01-02 15:04:05")
res.UpdatedAt = time.Unix(int64(lang.UpdatedAt), 0).Format("2006-01-02 15:04:05")
}

View File

@ -0,0 +1,89 @@
package logic
import (
"micro-bundle/internal/dao"
"micro-bundle/internal/model"
"micro-bundle/pb/bundle"
"time"
)
// CheckOrderEligibility 下单前置校验(先用后付)。
// 规则:
// 4. 套餐订单 + 先用后付 + 到期未付:禁止所有套餐下单
// 5. 套餐到期未付不影响增值下单(无限续杯)
// 3. 套餐有效期内只允许存在一笔
func CheckOrderEligibility(req *bundle.CheckOrderEligibilityRequest) (*bundle.CheckOrderEligibilityResponse, error) {
res := &bundle.CheckOrderEligibilityResponse{Allow: true}
if req == nil || req.CustomerID == "" {
res.Allow = false
res.Reason = "INVALID_PARAM"
res.Msg = "customerID 必填"
return res, nil
}
// 套餐下单校验(增值订单不受这些限制:无限续杯)
if req.OrderKind == model.OrderKindBundle {
// 1. 先用后付套餐逾期未付 → 不可再下单任何套餐(普通/先用后付)。
overdue, err := dao.ExistsOverduePayLaterBundle(req.CustomerID)
if err != nil {
return res, err
}
if overdue {
res.Allow = false
res.Reason = model.OrderGateReasonPayLaterBundleOverdue
res.Msg = "您有过期的先用后付订单,无法再次体验"
return res, nil
}
// 2. 已存在先用后付且未支付(待付款)的套餐订单 → 提示去支付。
pending, err := dao.ExistsPendingPayLaterBundle(req.CustomerID)
if err != nil {
return res, err
}
if pending {
res.Allow = false
res.Reason = model.OrderGateReasonPayLaterBundlePending
res.Msg = "当前已存在未支付订单,请去我的页面支付"
return res, nil
}
// 3. 有效期内已存在套餐订单 → 保证套餐有效期内只存在一笔。
exists, err := dao.ExistsActiveBundle(req.CustomerID)
if err != nil {
return res, err
}
if exists {
res.Allow = false
res.Reason = model.OrderGateReasonBundleActiveExists
res.Msg = "有效期内已存在套餐订单"
return res, nil
}
}
return res, nil
}
// MarkOverdueOrders 扫描到期订单(套餐 → 冻结;增值 → 余量作废)
func MarkOverdueOrders(req *bundle.MarkOverdueOrdersRequest) (*bundle.MarkOverdueOrdersResponse, error) {
res := &bundle.MarkOverdueOrdersResponse{}
now := time.Now()
if req != nil && req.Now != "" {
if t, err := time.ParseInLocation("2006-01-02 15:04:05", req.Now, time.Local); err == nil {
now = t
}
}
if n, err := dao.MarkBundleOverdue(now); err != nil {
res.Msg = err.Error()
return res, err
} else {
res.BundleOverdueCount = int32(n)
}
if n, err := dao.MarkValueAddExpiredByDue(now); err != nil {
res.Msg = err.Error()
return res, err
} else {
res.ValueAddExpiredCount = int32(n)
}
res.Msg = "ok"
return res, nil
}

View File

@ -16,6 +16,17 @@ import (
func CreateOrderRecord(req *bundle.OrderCreateRecord) (res *bundle.CommonResponse, err error) {
res = new(bundle.CommonResponse)
// 先用后付下单前置校验
if eligibility, e := CheckOrderEligibility(&bundle.CheckOrderEligibilityRequest{
CustomerID: req.CustomerID,
OrderKind: model.OrderKindBundle,
OrderMode: req.OrderMode,
}); e != nil {
return nil, e
} else if !eligibility.Allow {
res.Msg = eligibility.Msg
return res, errors.New(eligibility.Reason)
}
//orderUUID := app.ModuleClients.SfNode.Generate().Base64()
uuidV4, err := uuid.NewV4()
if err != nil {
@ -29,32 +40,52 @@ func CreateOrderRecord(req *bundle.OrderCreateRecord) (res *bundle.CommonRespons
}
var addRecords []model.BundleOrderValueAdd
for _, i := range req.AddRecords {
addOrderMode := i.OrderMode
if addOrderMode == 0 {
addOrderMode = model.OrderModePayLater // 增值订单默认先用后付
}
addPayLaterStatus := i.PayLaterStatus
if addOrderMode == model.OrderModePayLater && addPayLaterStatus == 0 && i.PaymentStatus != 2 {
addPayLaterStatus = model.PayLaterStatusPending
}
addRecords = append(addRecords, model.BundleOrderValueAdd{
UUID: app.ModuleClients.SfNode.Generate().Base64(),
OrderNo: orderNo,
OrderUUID: orderUUID,
CustomerID: req.CustomerID,
CustomerNum: req.CustomerNum,
CustomerName: req.CustomerName,
ServiceType: i.ServiceType,
CurrencyType: i.CurrencyType,
Amount: float64(i.Amount),
Num: i.Num,
Unit: i.Unit,
ValueAddUUID: i.ValueUid,
Source: int(i.Source),
PaymentStatus: int(i.PaymentStatus),
SignContract: req.SignContract,
Signature: req.Signature,
SignedTime: req.SignedTime,
Snapshot: req.Snapshot,
HandlingFee: i.HandlingFee,
EquityType: i.EquityType,
QuotaType: i.QuotaType,
QuotaValue: i.QuotaValue,
IsExpired: i.IsExpired,
UUID: app.ModuleClients.SfNode.Generate().Base64(),
OrderNo: orderNo,
OrderUUID: orderUUID,
CustomerID: req.CustomerID,
CustomerNum: req.CustomerNum,
CustomerName: req.CustomerName,
ServiceType: i.ServiceType,
CurrencyType: i.CurrencyType,
Amount: float64(i.Amount),
Num: i.Num,
Unit: i.Unit,
ValueAddUUID: i.ValueUid,
Source: int(i.Source),
PaymentStatus: int(i.PaymentStatus),
SignContract: req.SignContract,
Signature: req.Signature,
SignedTime: req.SignedTime,
Snapshot: req.Snapshot,
HandlingFee: i.HandlingFee,
EquityType: i.EquityType,
QuotaType: i.QuotaType,
QuotaValue: i.QuotaValue,
IsExpired: i.IsExpired,
OrderMode: addOrderMode,
DueTime: i.DueTime,
PayLaterStatus: addPayLaterStatus,
ContractTplType: i.ContractTplType,
})
}
orderMode := req.OrderMode
if orderMode == 0 {
orderMode = model.OrderModeNormal
}
payLaterStatus := req.PayLaterStatus
if orderMode == model.OrderModePayLater && payLaterStatus == 0 && req.Status != model.BundleStatusPaid {
payLaterStatus = model.PayLaterStatusPending
}
orderRecord := &model.BundleOrderRecords{
UUID: orderUUID,
OrderNo: orderNo,
@ -82,6 +113,10 @@ func CreateOrderRecord(req *bundle.OrderCreateRecord) (res *bundle.CommonRespons
InviterID: req.InviterId,
PurchaseType: req.PurchaseType,
RenewalOrderUUID: req.RenewalOrderUUID,
OrderMode: orderMode,
DueTime: req.DueTime,
PayLaterStatus: payLaterStatus,
ContractTplType: req.ContractTplType,
}
res, err = dao.CreateOrderRecord(orderRecord)
return

View File

@ -67,8 +67,8 @@ func GetArtistUploadStatsList(req *dto.TaskQueryRequest) ([]*dto.ArtistUploadSta
}
// GetPendingAssignBySubNums 查询指定艺人的可指派数量
func GetPendingAssignBySubNums(subNums []string, page int, pageSize int) ([]*dto.ArtistPendingAssignItem, int64, error) {
return dao.GetPendingAssignBySubNums(subNums, page, pageSize)
func GetPendingAssignBySubNums(req *dto.PendingAssignQueryRequest) ([]*dto.ArtistPendingAssignItem, int64, error) {
return dao.GetPendingAssignBySubNums(req)
}
// AssignTask 指派某位员工完成某个艺人的任务
@ -88,7 +88,10 @@ func AssignTask(req *dto.TaskAssignRequest) error {
return commonErr.ReturnError(nil, "指派数量不能全部为0", "至少有一个指派数量需大于0")
}
progressTaskCount, completeTaskCount, err := dao.GetArtistTaskStatsBySubNum(req.SubNum)
if req.BundleTaskType != model.BundleTaskTypePayLater {
req.BundleTaskType = model.BundleTaskTypeNormal
}
progressTaskCount, completeTaskCount, err := dao.GetArtistTaskStatsBySubNum(req.SubNum, req.BundleTaskType)
if err != nil {
// 查询不到的话,给一个默认值
progressTaskCount, completeTaskCount = 0, 0
@ -190,6 +193,7 @@ func GetEmployeeAssignedTasks(req *dto.EmployeeTaskQueryRequest) ([]*dto.TaskAss
CompleteDataCount: record.CompleteDataCount,
CompleteReportCount: record.CompleteReportCount,
TaskBatch: record.TaskBatch,
BundleTaskType: record.BundleTaskType,
UpdatedAt: record.UpdatedAt,
})
}
@ -220,6 +224,7 @@ func GetEmployeeAssignedTasks(req *dto.EmployeeTaskQueryRequest) ([]*dto.TaskAss
PendingVideoScriptCount: record.PendingVideoScriptCount,
PendingReportCount: record.PendingReportCount,
TaskBatch: record.TaskBatch,
BundleTaskType: record.BundleTaskType,
UpdatedAt: record.UpdatedAt,
})
}
@ -345,6 +350,7 @@ func GetTaskAssignRecordsList(req *dto.TaskAssignRecordsQueryRequest) ([]*dto.Ta
CompletePostCount: record.CompletePostCount,
CompleteDataCount: record.CompleteDataCount,
CompleteReportCount: record.CompleteReportCount,
BundleTaskType: record.BundleTaskType,
UpdatedAt: record.UpdatedAt,
})
}

View File

@ -0,0 +1,233 @@
package logic
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"micro-bundle/internal/dto"
)
// ---- CreateTaskWorkLog 范围校验 ----
func TestCreateTaskWorkLog_Validation(t *testing.T) {
t.Parallel()
base := func() *dto.CreateTaskWorkLogRequest {
return &dto.CreateTaskWorkLogRequest{
AssignRecordsUUID: "uuid-1",
WorkUUID: "work-1",
OperationType: 1, // 1-加
TaskType: 1, // 1-视频
TaskCount: 1,
}
}
tests := []struct {
name string
mutate func(r *dto.CreateTaskWorkLogRequest)
wantContain string
}{
// ReturnError(nil, "参数错误", ...) 实际返回的 msg 统一是 "参数错误",
// 详细说明只在 print 字段(不入错误链),所以断言统一为 "参数错误",
// 区分点放在测试名上。
{"OperationType为0", func(r *dto.CreateTaskWorkLogRequest) { r.OperationType = 0 }, "参数错误"},
{"OperationType为5", func(r *dto.CreateTaskWorkLogRequest) { r.OperationType = 5 }, "参数错误"},
{"TaskType为0", func(r *dto.CreateTaskWorkLogRequest) { r.TaskType = 0 }, "参数错误"},
{"TaskType为7", func(r *dto.CreateTaskWorkLogRequest) { r.TaskType = 7 }, "参数错误"},
{"TaskCount为负", func(r *dto.CreateTaskWorkLogRequest) { r.TaskCount = -1 }, "参数错误"},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
r := base()
tc.mutate(r)
err := CreateTaskWorkLog(r)
require.Error(t, err)
assert.Contains(t, err.Error(), tc.wantContain)
})
}
}
// ---- BatchAssignTask 校验 ----
func TestBatchAssignTask_EmptyItems(t *testing.T) {
t.Parallel()
// nil 和 空 slice 都走 `ReturnError(nil, "参数错误", "批量指派项不能为空")`,
// 实际返回的 msg 是 "参数错误"。
// nil slice
err := BatchAssignTask(nil)
require.Error(t, err)
assert.Contains(t, err.Error(), "参数错误")
// 空 slice
err = BatchAssignTask([]*dto.BatchAssignItem{})
require.Error(t, err)
assert.Contains(t, err.Error(), "参数错误")
}
func TestBatchAssignTask_NilItem(t *testing.T) {
t.Parallel()
// 同样 `ReturnError(nil, "参数错误", "存在空的指派项")`。
err := BatchAssignTask([]*dto.BatchAssignItem{nil})
require.Error(t, err)
assert.Contains(t, err.Error(), "参数错误")
}
func TestBatchAssignTask_NegativeCount(t *testing.T) {
t.Parallel()
make := func(mutate func(*dto.BatchAssignItem)) *dto.BatchAssignItem {
it := &dto.BatchAssignItem{
SubNum: "S001",
TaskAssignee: "员工A",
TaskAssigneeNum: "E001",
AssignVideoCount: 1,
}
mutate(it)
return it
}
tests := []struct {
name string
mutate func(*dto.BatchAssignItem)
}{
{"Video为负", func(it *dto.BatchAssignItem) { it.AssignVideoCount = -1 }},
{"Post为负", func(it *dto.BatchAssignItem) { it.AssignPostCount = -1 }},
{"Data为负", func(it *dto.BatchAssignItem) { it.AssignDataCount = -1 }},
{"VideoScript为负", func(it *dto.BatchAssignItem) { it.AssignVideoScriptCount = -1 }},
{"Report为负", func(it *dto.BatchAssignItem) { it.AssignReportCount = -1 }},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
err := BatchAssignTask([]*dto.BatchAssignItem{make(tc.mutate)})
require.Error(t, err)
assert.Contains(t, err.Error(), "指派数量不能小于0")
})
}
}
func TestBatchAssignTask_SingleItemAllZero(t *testing.T) {
t.Parallel()
it := &dto.BatchAssignItem{SubNum: "S001", TaskAssignee: "员工A", TaskAssigneeNum: "E001"}
err := BatchAssignTask([]*dto.BatchAssignItem{it})
require.Error(t, err)
assert.Contains(t, err.Error(), "指派数量不能全部为0")
}
// 注:logic 中"全部指派项数量都为 0"的外层兜底(`if !hasPositive`)实际不可达:
// 循环中每项的"单项全 0"检查会先返回,所以该分支永远走不到。
// 保留这条注释供后人参考,不写不可达测试。
// ---- TerminateTaskByUUID / BatchTerminateTaskByUUIDs ----
func TestTerminateTaskByUUID_Empty(t *testing.T) {
t.Parallel()
err := TerminateTaskByUUID("")
require.Error(t, err)
assert.Contains(t, err.Error(), "参数错误")
}
func TestBatchTerminateTaskByUUIDs_Empty(t *testing.T) {
t.Parallel()
success, failed, failedUUIDs, err := BatchTerminateTaskByUUIDs(nil)
require.Error(t, err)
assert.Equal(t, 0, success)
assert.Equal(t, 0, failed)
assert.Nil(t, failedUUIDs)
assert.Contains(t, err.Error(), "参数错误")
success, failed, failedUUIDs, err = BatchTerminateTaskByUUIDs([]string{})
require.Error(t, err)
assert.Equal(t, 0, success)
assert.Equal(t, 0, failed)
assert.Nil(t, failedUUIDs)
}
// ---- RevertTaskCompletionByUUIDItem ----
func TestRevertTaskCompletionByUUIDItem_Empty(t *testing.T) {
t.Parallel()
for _, in := range []string{"", " ", "\t\n"} {
err := RevertTaskCompletionByUUIDItem(in)
require.Error(t, err, "input=%q", in)
assert.Contains(t, err.Error(), "参数错误")
}
}
// ---- GetTaskActualStatusByUUID ----
func TestGetTaskActualStatusByUUID_Empty(t *testing.T) {
t.Parallel()
for _, in := range []string{"", " "} {
status, err := GetTaskActualStatusByUUID(in)
require.Error(t, err, "input=%q", in)
assert.Equal(t, 0, status)
assert.Contains(t, err.Error(), "查询参数错误")
}
}
// ---- AddHiddenTaskAssignee ----
func TestAddHiddenTaskAssignee_Empty(t *testing.T) {
t.Parallel()
tests := []struct {
name, assignee, num string
}{
{"都为空", "", ""},
{"名称空", "", "E001"},
{"账号空", "员工A", ""},
{"全空格", " ", " "},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
err := AddHiddenTaskAssignee(tc.assignee, tc.num)
require.Error(t, err)
assert.Contains(t, err.Error(), "参数错误")
})
}
}
// ---- buildDefaultPendingLayout (私有函数,同包内可直接测) ----
func TestBuildDefaultPendingLayout(t *testing.T) {
t.Parallel()
got := buildDefaultPendingLayout()
require.NotEmpty(t, got)
var rows []map[string]any
err := json.Unmarshal([]byte(got), &rows)
require.NoError(t, err, "应当是合法 JSON")
// 33 个字段是当前硬编码的默认值;如未来调整,只改这一个数字。
assert.Len(t, rows, 33)
// 每个字段必有 fieldKey / fieldValue / sortOrNot / status
for i, row := range rows {
assert.Contains(t, row, "fieldKey", "第 %d 行缺 fieldKey", i)
assert.Contains(t, row, "fieldValue", "第 %d 行缺 fieldValue", i)
assert.Contains(t, row, "sortOrNot", "第 %d 行缺 sortOrNot", i)
assert.Contains(t, row, "status", "第 %d 行缺 status", i)
}
}
// GetPendingTaskLayout 的 dao 降级路径(失败时回退默认布局)留作下一阶段,
// 那里需要 sqlmock 配合 dao 验证。纯函数 buildDefaultPendingLayout 已被
// TestBuildDefaultPendingLayout 覆盖。

View File

@ -8,6 +8,16 @@ import (
"gorm.io/plugin/soft_delete"
)
const (
BundleTypeBasic = 1 // 基础套餐
BundleTypePostpaid = 2 // 先用后付套餐
)
const (
BundleShelfStatusUp = 1 // 上架
BundleShelfStatusDown = 2 // 下架
)
type BundleProfile struct {
gorm.Model
UUID string `json:"uuid" gorm:"column:uuid;type:varchar(1024);comment:套餐UUID"`
@ -27,20 +37,23 @@ type BundleProfile struct {
FontColor string `json:"fontColor" gorm:"column:font_color;type:varchar(32);comment:字体颜色"`
BgImg1 string `json:"bgImg1" gorm:"column:bg_img1;type:varchar(1024);comment:背景图-首页"`
BgImg2 string `json:"bgImg2" gorm:"column:bg_img2;type:varchar(1024);comment:背景图-我的"`
BundleType int64 `json:"bundleType" gorm:"column:bundle_type;type:int;default:1;comment:套餐类型 1:基础套餐 2:先用后付套餐"`
PayLaterTime int64 `json:"payLaterTime" gorm:"column:pay_later_time;type:int;default:1;comment:先用后付套餐的后付时间,单位为天"`
BundleToValueAddService []BundleToValueAddService `gorm:"foreignKey:BundleUuid;references:UUID" json:"bundleToValueAddService"`
BundleProfileLang []BundleProfileLang `gorm:"foreignKey:UUID;references:UUID" json:"bundleProfileLang"`
}
type BundleProfileLang struct {
Id int32 `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT" json:"id"`
UUID string `json:"uuid" gorm:"column:uuid;type:varchar(1024);comment:套餐UUID"`
Name string `json:"name" gorm:"column:name;type:varchar(2048);comment:套餐名称"`
Price float32 `json:"price" gorm:"column:price;type:decimal(12,2);comment:套餐价格"`
PriceType int64 `json:"priceType" gorm:"column:price_type;type:int;comment:套餐类型 1:人民币 2:美元"`
Content string `json:"content" gorm:"column:content;type:text;comment:套餐内容"`
Language string `json:"language" gorm:"column:language;type:varchar(32);comment:套餐语言 zh-CN zh-TW EN de-DE ja-JP(中繁英德日)"`
CreatedAt int64 `gorm:"column:created_at;autoCreateTime"`
UpdatedAt int64 `gorm:"column:updated_at;autoCreateTime"`
DeletedAt soft_delete.DeletedAt
Id int32 `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT" json:"id"`
UUID string `json:"uuid" gorm:"column:uuid;type:varchar(1024);comment:套餐UUID"`
Name string `json:"name" gorm:"column:name;type:varchar(2048);comment:套餐名称"`
Price float32 `json:"price" gorm:"column:price;type:decimal(12,2);comment:套餐价格"`
PriceType int64 `json:"priceType" gorm:"column:price_type;type:int;comment:套餐类型 1:人民币 2:美元"`
Content string `json:"content" gorm:"column:content;type:text;comment:套餐内容"`
PayLaterTime int64 `json:"payLaterTime" gorm:"column:pay_later_time;type:int;default:1;comment:先用后付套餐的后付时间,单位为天"`
Language string `json:"language" gorm:"column:language;type:varchar(32);comment:套餐语言 zh-CN zh-TW EN de-DE ja-JP(中繁英德日)"`
CreatedAt int64 `gorm:"column:created_at;autoCreateTime"`
UpdatedAt int64 `gorm:"column:updated_at;autoCreateTime"`
DeletedAt soft_delete.DeletedAt
}
type BundleToValueAddService struct {
Id int32 `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT" json:"id"`
@ -175,11 +188,12 @@ type UserBundleBalancePo struct {
type BundleBalance struct {
gorm.Model
UserId int `gorm:"column:user_id;not null"`
Month string `gorm:"column:month;type:varchar(32);not null;index:idx_month"`
OrderUUID string `gorm:"column:order_uuid;type:varchar(1024);not null;index:idx_order_uuid"`
ExpiredAt time.Time `gorm:"column:expired_at;type:datetime;comment:套餐过期时间;index:idx_expired_at"`
StartAt time.Time `gorm:"column:start_at;type:datetime;comment:套餐开始时间;index:idx_start_at"`
UserId int `gorm:"column:user_id;not null"`
Month string `gorm:"column:month;type:varchar(32);not null;index:idx_month"`
OrderUUID string `gorm:"column:order_uuid;type:varchar(1024);not null;index:idx_order_uuid"`
ExpiredAt time.Time `gorm:"column:expired_at;type:datetime;comment:套餐过期时间;index:idx_expired_at"`
StartAt time.Time `gorm:"column:start_at;type:datetime;comment:套餐开始时间;index:idx_start_at"`
BundleType int `gorm:"column:bundle_type;type:int;default:1;comment:套餐类型 1:基础套餐 2:先用后付套餐"`
// 套餐与增值账号
BundleAccountNumber int `gorm:"column:bundle_account_number;not null;comment:套餐账号总数"`
@ -347,6 +361,7 @@ func (*BundleActivate) TableName() string {
type BundleBalanceUsePo struct {
UserId int
OrderUUID string
AccountNumber int
VideoNumber int
ImageNumber int

View File

@ -48,6 +48,10 @@ type BundleOrderRecords struct {
InviterID uint64 `gorm:"column:inviter_id;type:bigint;comment:邀请人ID" json:"inviterID"`
PurchaseType uint64 `gorm:"column:purchase_type;type:bigint;comment:购买类型 1:新购 2:续费" json:"purchaseType"`
RenewalOrderUUID string `gorm:"column:renewal_order_uuid;type:varchar(1024);comment:续费订单UUID" json:"renewalOrderUUID"`
OrderMode int32 `gorm:"column:order_mode;type:int;default:1;comment:订单模式 1:普通 2:先用后付;index:idx_customer_order_mode,priority:2" json:"orderMode"`
DueTime string `gorm:"column:due_time;type:varchar(64);comment:先用后付到期应付时间" json:"dueTime"`
PayLaterStatus int32 `gorm:"column:pay_later_status;type:int;default:0;comment:先用后付状态 0:无 1:待付款 2:已付款 3:逾期未付;index:idx_paylater_status" json:"payLaterStatus"`
ContractTplType int32 `gorm:"column:contract_tpl_type;type:int;default:0;comment:合同模板类型 1:套餐普通 2:套餐先用后付 3:增值先用后付" json:"contractTplType"`
}
type BundleOrderValueAdd struct {
gorm.Model
@ -78,6 +82,10 @@ type BundleOrderValueAdd struct {
QuotaType int32 `json:"quotaType" gorm:"column:quota_type;type:int;default:1;comment:额度类型 1:不限额度 2:每月限额度"`
QuotaValue int32 `json:"quotaValue" gorm:"column:quota_value;type:int;comment:额度值"`
IsExpired bool `json:"isExpired" gorm:"column:is_expired;default:false;comment:是否过期作废 false:不作废 true:作废"`
OrderMode int32 `json:"orderMode" gorm:"column:order_mode;type:int;default:2;comment:订单模式 1:普通 2:先用后付(增值默认2);index:idx_valueadd_order_mode"`
DueTime string `json:"dueTime" gorm:"column:due_time;type:varchar(64);comment:先用后付到期应付时间"`
PayLaterStatus int32 `json:"payLaterStatus" gorm:"column:pay_later_status;type:int;default:0;comment:先用后付状态 0:无 1:待付款 2:已付款 3:逾期未付;index:idx_valueadd_paylater_status"`
ContractTplType int32 `json:"contractTplType" gorm:"column:contract_tpl_type;type:int;default:0;comment:合同模板类型 3:增值先用后付"`
}
type PlatformIDs []uint32
@ -109,3 +117,45 @@ const (
ConfirmationNotConfirmed = 1
ConfirmationConfirmed = 2
)
// 订单模式
const (
OrderModeNormal int32 = 1 // 普通订单
OrderModePayLater int32 = 2 // 先用后付订单
)
// 套餐订单 Status
const (
BundleStatusSignedUnpaid int64 = 1 // 已签未支付
BundleStatusPaid int64 = 2 // 已签已支付
BundleStatusOverdue int64 = 3 // 先用后付到期未付
)
// 先用后付状态
const (
PayLaterStatusNone int32 = 0
PayLaterStatusPending int32 = 1 // 待付款
PayLaterStatusPaid int32 = 2 // 已付款
PayLaterStatusOverdue int32 = 3 // 逾期未付
)
// 合同模板类型
const (
ContractTplBundleNormal int32 = 1 // 套餐-普通
ContractTplBundlePayLater int32 = 2 // 套餐-先用后付
ContractTplValueAddPayLater int32 = 3 // 增值-先用后付
)
// 下单校验业务类型
const (
OrderKindBundle int32 = 1
OrderKindValueAdd int32 = 2
)
// 下单校验错误码
const (
OrderGateReasonPayLaterBundleOverdue = "PAYLATER_BUNDLE_OVERDUE"
OrderGateReasonPayLaterBundlePending = "PAYLATER_BUNDLE_PENDING"
OrderGateReasonBundleActiveExists = "BUNDLE_ACTIVE_EXISTS"
OrderGateReasonValueAddLocked = "VALUE_ADD_OVERDUE_LOCKED"
)

View File

@ -1,6 +1,8 @@
package model
import (
"time"
"gorm.io/plugin/soft_delete"
)
@ -72,16 +74,18 @@ type CastWork struct {
ArtistPhoneAreaCode string `gorm:"column:artist_phone_area_code;type:varchar(50);NOT NULL;default:'';comment:'艺人手机号区号'" json:"artistPhoneAreaCode"`
WorkCategory int8 `gorm:"column:work_category;type:tinyint(1);NOT NULL;default:1;comment: 1 图文 2 视频" json:"workCategory"`
SubmitTime string `gorm:"column:submit_time;type:varchar(50);NOT NULL;default:'';comment:提交时间" json:"submitTime"`
ArtistSubNum string `gorm:"column:artist_sub_num;type:varchar(50);NOT NULL;default:'';comment:艺人订阅号" json:"artistSubNum"`
StatusUpdateTime string `gorm:"column:status_update_time;type:varchar(50);NOT NULL;default:'';comment:状态更新时间" json:"statusUpdateTime"`
PlatformIDs string `gorm:"column:platform_ids;type:json;NOT NULL;comment:发布平台ID集合 TIKTOK= 1, YOUTUBE = 2, INS = 3;" json:"platformIDs"`
Status int8 `gorm:"column:status;type:tinyint(1);NOT NULL;default:1;comment: 1 待提交 2 审核中 3 审核失败 4 待艺人确认 5 艺人驳回 6 发布成功 7 发布失败 8 待发布" json:"status"`
Title string `gorm:"column:title;type:varchar(50);NOT NULL;" json:"title"`
Title string `gorm:"column:title;type:varchar(300);NOT NULL;" json:"title"`
Content string `gorm:"column:content;type:varchar(2000);NOT NULL;" json:"content"`
Cost int8 `gorm:"column:cost;type:tinyint(1);NOT NULL;default:0;comment: 1 消耗 2 未消耗" json:"cost"`
ConfirmRemark string `gorm:"column:confirm_remark;type:varchar(300);NOT NULL;default:'';comment:艺人确认备注" json:"confirmRemark"`
PublishConfig string `gorm:"column:publish_config;type:varchar(600);NOT NULL;comment:发布配置" json:"publishConfig"`
ApprovalID string `gorm:"column:approval_id;type:varchar(50);NOT NULL;default:'';comment:审批ID" json:"approvalID"`
Source int8 `gorm:"column:source;type:tinyint(1);NOT NULL;default:1;comment: 1 系统 2 导入" json:"source"`
OriginUuid string `gorm:"column:origin_uuid;type:varchar(50);NOT NULL;default:'';comment:来源uuid;index:idx_origin_uuid" json:"originUuid"`
CreatedAt int `gorm:"column:created_at;type:int(11);autoCreateTime" json:"createdAt"`
UpdatedAt int `gorm:"column:updated_at;type:int(11);autoCreateTime" json:"updatedAt"`
DeletedAt soft_delete.DeletedAt
@ -155,10 +159,28 @@ func (WorkVideo) TableName() string {
type WorkExtra struct {
WorkUuid string `gorm:"column:work_uuid;type:varchar(50);NOT NULL;index:idx_work_uuid;default:'';comment:作品uuid;primary_key" json:"workUuid"`
ArtistConfirmedTime int64 `gorm:"column:artist_confirmed_time;type:bigint(20);NOT NULL;default:0;comment:艺人确认时间" json:"artistConfirmedTime"`
ScriptUuid string `gorm:"column:script_uuid;type:varchar(50);NOT NULL;default:'';comment:脚本uuid;index:idx_script_uuid" json:"scriptUuid"`
CostType uint32 `gorm:"column:cost_type;type:tinyint(3);NOT NULL;default:1;comment:计费类型 1 套餐 2 增值" json:"costType"`
CreatedAt int `gorm:"column:created_at;type:int(11);autoCreateTime" json:"createdAt"`
UpdatedAt int `gorm:"column:updated_at;type:int(11);autoCreateTime" json:"updatedAt"`
DeletedAt soft_delete.DeletedAt
ScriptInfo string `gorm:"column:script_info;type:varchar(1500);NOT NULL;default:'';comment:脚本数据" json:"scriptInfo"`
PublishStatus uint32 `gorm:"column:publish_status;type:tinyint(3);NOT NULL;default:0;comment:抖音发布状态 0 未发布 1 发布中 2 发布完成 3 发布失败 4 异常" json:"publishStatus"`
//TiktokStatus uint32 `gorm:"column:tiktok_status;type:tinyint(3);NOT NULL;default:0;comment:抖音发布状态 0 未发布 1 发布中 2 发布完成 3 发布失败 4 异常" json:"tiktokStatus"`
//InsStatus uint32 `gorm:"column:ins_status;type:tinyint(3);NOT NULL;default:0;comment:ins发布状态 0 未发布 1 发布中 2 发布完成 3 发布失败 4 异常" json:"insStatus"`
//DmStatus uint32 `gorm:"column:dm_status;type:tinyint(3);NOT NULL;default:0;comment:dm发布状态 0 未发布 1 发布中 2 发布完成 3 发布失败 4 异常" json:"dmStatus"`
//PublishMediaID string `gorm:"column:publish_media_id;type:varchar(100);NOT NULL;default:'';comment:发布媒体ID" json:"publishMediaId"`
ConfirmType uint32 `gorm:"column:confirm_type;type:tinyint(3);NOT NULL;default:0;comment:确认类型 1 艺人确认 2系统确认" json:"confirmType"`
ConfirmFailType uint32 `gorm:"column:confirm_fail_type;type:tinyint(3);NOT NULL;default:0;comment:0 未知 1 余额不足 2 过期" json:"confirmFailType"`
RepublishPlatformIDs string `gorm:"column:republish_platform_ids;type:varchar(100);NOT NULL;default:'';comment:重发平台ID" json:"republishPlatformIds"`
NeedPublishPlatformIDs string `gorm:"column:need_publish_platform_ids;type:varchar(100);NOT NULL;default:'';comment:需要发布的平台ID" json:"needPublishPlatformIds"`
BundleOrderUuid string `gorm:"column:bundle_order_uuid;type:varchar(50);NOT NULL;default:'';comment:套餐订单uuid" json:"bundleOrderUuid"`
OrderNo string `gorm:"column:order_no;type:varchar(50);NOT NULL;default:'';index:idx_order_no;comment:订单号" json:"orderNo"`
OrderStatus uint8 `gorm:"column:order_status;type:tinyint(1);NOT NULL;default:0;comment:订单状态 1:待支付 2:已支付 3:已过期" json:"orderStatus"`
BillingType uint8 `gorm:"column:billing_type;type:tinyint(1);NOT NULL;default:0;comment:计费类型 0:普通视频 1:先用后付视频" json:"billingType"`
TagInfo string `gorm:"column:tag_info;type:varchar(1000);NOT NULL;default:'';comment:话题标签信息JSON格式" json:"tagInfo"`
CreatedAt int `gorm:"column:created_at;type:int(11);autoCreateTime" json:"createdAt"`
UpdatedAt int `gorm:"column:updated_at;type:int(11);autoCreateTime" json:"updatedAt"`
DeletedAt soft_delete.DeletedAt
}
func (WorkExtra) TableName() string {
@ -218,28 +240,56 @@ func (CastWorkAnalysisExtra) TableName() string {
// VideoScript 视频脚本表
type VideoScript struct {
Uuid string `gorm:"column:uuid;type:varchar(50);NOT NULL;primary_key;comment:脚本UUID" json:"uuid"`
Title string `gorm:"column:title;type:varchar(200);NOT NULL;default:'';comment:脚本标题" json:"title"`
Content string `gorm:"column:content;type:varchar(1000);NOT NULL;default:'';comment:脚本内容" json:"content"`
ArtistUuid string `gorm:"column:artist_uuid;type:varchar(50);NOT NULL;default:'';comment:艺人UUID" json:"artistUuid"`
ArtistName string `gorm:"column:artist_name;type:varchar(50);NOT NULL;default:'';comment:艺人名称" json:"artistName"`
ArtistNum string `gorm:"column:artist_num;type:varchar(50);NOT NULL;default:'';comment:艺人编号" json:"artistNum"`
ArtistPhone string `gorm:"column:artist_phone;type:varchar(50);NOT NULL;default:'';comment:艺人手机号" json:"artistPhone"`
CreatorUuid string `gorm:"column:creator_uuid;type:varchar(50);NOT NULL;default:'';comment:创建人UUID" json:"creatorUuid"`
CreatorName string `gorm:"column:creator_name;type:varchar(50);NOT NULL;default:'';comment:创建人名称" json:"creatorName"`
ApprovalID string `gorm:"column:approval_id;type:varchar(50);NOT NULL;default:'';comment:审批ID" json:"approvalId"`
ApprovalReply string `gorm:"column:approval_reply;type:varchar(500);NOT NULL;default:'';comment:审批回复" json:"approvalReply"`
Status uint8 `gorm:"column:status;type:tinyint(1);NOT NULL;default:1;comment:状态 1:草稿 2:审核中 3:审核通过 4:审核不通过" json:"status"`
Remark string `gorm:"column:remark;type:varchar(500);NOT NULL;default:'';comment:备注" json:"remark"`
CreatedAt int `gorm:"column:created_at;type:int(11);autoCreateTime" json:"createdAt"`
UpdatedAt int `gorm:"column:updated_at;type:int(11);autoUpdateTime" json:"updatedAt"`
DeletedAt soft_delete.DeletedAt
Uuid string `gorm:"column:uuid;type:varchar(50);NOT NULL;primary_key;comment:脚本UUID" json:"uuid"`
Title string `gorm:"column:title;type:varchar(200);NOT NULL;default:'';comment:脚本标题" json:"title"`
Content string `gorm:"column:content;type:varchar(1000);NOT NULL;default:'';comment:脚本内容" json:"content"`
ArtistUuid string `gorm:"column:artist_uuid;type:varchar(50);NOT NULL;default:'';comment:艺人UUID" json:"artistUuid"`
ArtistName string `gorm:"column:artist_name;type:varchar(50);NOT NULL;default:'';comment:艺人名称" json:"artistName"`
ArtistNum string `gorm:"column:artist_num;type:varchar(50);NOT NULL;default:'';comment:艺人编号" json:"artistNum"`
ArtistPhone string `gorm:"column:artist_phone;type:varchar(50);NOT NULL;default:'';comment:艺人手机号" json:"artistPhone"`
CreatorUuid string `gorm:"column:creator_uuid;type:varchar(50);NOT NULL;default:'';comment:创建人UUID" json:"creatorUuid"`
CreatorName string `gorm:"column:creator_name;type:varchar(50);NOT NULL;default:'';comment:创建人名称" json:"creatorName"`
ApprovalID string `gorm:"column:approval_id;type:varchar(50);NOT NULL;default:'';comment:审批ID" json:"approvalId"`
ApprovalReply string `gorm:"column:approval_reply;type:varchar(500);NOT NULL;default:'';comment:审批回复" json:"approvalReply"`
ApprovalTime *time.Time `gorm:"column:approval_time;type:datetime;comment:审批时间" json:"approvalTime"`
QuoteCount int `gorm:"column:quote_count;type:int(11);NOT NULL;default:0;comment:引用次数" json:"quoteCount"`
Status uint8 `gorm:"column:status;type:tinyint(1);NOT NULL;default:1;comment:状态 1:草稿 2:审核中 3:审核通过 4:审核不通过" json:"status"`
Remark string `gorm:"column:remark;type:varchar(500);NOT NULL;default:'';comment:备注" json:"remark"`
OrderNo string `gorm:"column:order_no;type:varchar(50);NOT NULL;default:'';index:idx_order_no;comment:订单号" json:"orderNo"`
OrderStatus uint8 `gorm:"column:order_status;type:tinyint(1);NOT NULL;default:0;comment:订单状态 1:待支付 2:已支付 3:已过期" json:"orderStatus"`
BillingType uint8 `gorm:"column:billing_type;type:tinyint(1);NOT NULL;default:0;comment:计费类型 0:普通脚本 1:先用后付脚本" json:"billingType"`
CreatedAt int `gorm:"column:created_at;type:int(11);autoCreateTime" json:"createdAt"`
UpdatedAt int `gorm:"column:updated_at;type:int(11);autoUpdateTime" json:"updatedAt"`
DeletedAt soft_delete.DeletedAt
}
func (VideoScript) TableName() string {
return "cast_video_script"
}
// 脚本状态常量
const (
ScriptStatusDraft = 1 // 草稿
ScriptStatusReviewing = 2 // 审核中
ScriptStatusApproved = 3 // 审核通过
ScriptStatusRejected = 4 // 审核不通过
)
// 订单状态常量
const (
OrderStatusPending = 1 // 待支付
OrderStatusPaid = 2 // 已支付
OrderStatusExpired = 3 // 已过期
)
// 计费类型常量
const (
BillingTypeNormal = 0 // 普通脚本计费
BillingTypeBNPL = 1 // 先用后付脚本计费
)
// 竞品报告表
type CastCompetitiveReport struct {
Uuid string `gorm:"column:uuid;type:varchar(50);NOT NULL;primary_key;" json:"id"`

View File

@ -62,6 +62,8 @@ type Contract struct {
OperatorNum string `gorm:"column:operator_num;comment:操作人账号;index:idx_operator_num" json:"operatorNum"`
OperatorTime time.Time `gorm:"column:operator_time;comment:操作时间;index:idx_operator_time;" json:"operatorTime"`
ContractTemplateType int `gorm:"column:contract_template_type;type:int;default:0;comment:合同模板类型 1:套餐普通 2:套餐先用后付 3:增值先用后付;index:idx_contract_tpl_type" json:"contractTemplateType"`
PaymentCycles []ContractPaymentCycle `gorm:"foreignKey:ContractUUID;references:ContractUUID" json:"paymentCycles"`
DevelopmentCycles []DevelopmentCycle `gorm:"foreignKey:ContractUUID;references:ContractUUID" json:"developmentCycles"`

View File

@ -6,6 +6,12 @@ import (
"gorm.io/plugin/soft_delete"
)
// 任务所属套餐类型(对应 DB 列 bundle_task_type)
const (
BundleTaskTypeNormal = 1 // 基础套餐
BundleTaskTypePayLater = 2 // 先用后付套餐
)
var OrderByDataAnalysis = map[string]string{
"uploaded_data_analysis_count": "uploaded_data_count",
"bundle_data_analysis_total": "bundle_data_total",
@ -34,6 +40,7 @@ type TaskManagement struct {
TaskAssigneeNum string `gorm:"column:task_assignee_num;comment:最后一次的任务指派人账号" json:"taskAssigneeNum"`
ProgressCount int `gorm:"column:progress_count;comment:进行中的任务数量" json:"progressCount"`
CompleteCount int `gorm:"column:complete_count;comment:已完成的任务数量" json:"completeCount"`
BundleTaskType int `gorm:"column:bundle_task_type;type:tinyint;not null;default:1;comment:任务所属套餐类型 1:基础套餐 2:先用后付套餐;index:idx_bundle_task_type" json:"bundleTaskType"`
CreatedAt time.Time `gorm:"column:created_at;comment:创建时间" json:"createdAt"`
UpdatedAt time.Time `gorm:"column:updated_at;comment:更新时间" json:"updatedAt"`
DeletedAt soft_delete.DeletedAt `gorm:"column:deleted_at;type:int(11);index:idx_deleted_at" json:"deletedAt"`
@ -79,6 +86,7 @@ type TaskAssignRecords struct {
CompletePostCount int `gorm:"column:complete_post_count;comment:已完成图文数" json:"completePostCount"`
CompleteDataCount int `gorm:"column:complete_data_count;comment:已完成数据数" json:"completeDataCount"`
CompleteReportCount int `gorm:"column:complete_report_count;comment:已完成竞品报告数" json:"completeReportCount"`
BundleTaskType int `gorm:"column:bundle_task_type;type:tinyint;not null;default:1;comment:任务所属套餐类型 1:基础套餐 2:先用后付套餐;index:idx_bundle_task_type" json:"bundleTaskType"`
CreatedAt time.Time `gorm:"column:created_at;comment:创建时间" json:"createdAt"`
UpdatedAt time.Time `gorm:"column:updated_at;comment:更新时间;index:idx_updated_at" json:"updatedAt"`
DeletedAt soft_delete.DeletedAt `gorm:"column:deleted_at;type:int(11);index:idx_assign_deleted_at" json:"deletedAt"`
@ -157,6 +165,8 @@ type TaskWorkLog struct {
CreatedAt int `gorm:"column:created_at;type:int(11);comment:任务日志创建时间" json:"taskCreatedAt"`
UpdatedAt int `gorm:"column:updated_at;type:int(11);comment:任务日志更新时间" json:"taskUpdatedAt"`
DeletedAt soft_delete.DeletedAt `gorm:"column:deleted_at;type:int(11);index:idx_work_log_deleted_at" json:"taskDeletedAt"`
BundleTaskType int `gorm:"column:bundle_task_type;type:tinyint;not null;default:1;comment:任务所属套餐类型 1:基础套餐 2:先用后付套餐;index:idx_bundle_task_type" json:"bundleTaskType"`
}
func (t *TaskWorkLog) TableName() string {

View File

@ -35,7 +35,7 @@ type ValueAddService struct {
Id int32 `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT" json:"id"`
UUID string `json:"uuid" gorm:"column:uuid;type:varchar(1024);comment:增值套餐UUID"`
ServiceName string `json:"serviceName" gorm:"column:service_name;type:varchar(1024);comment:增值服务名称"`
ServiceType int32 `json:"serviceType" gorm:"column:service_type;type:int;comment:服务类型 1:视频 2:图文 3:数据报表 4:账号数 5:可用时长"`
ServiceType int32 `json:"serviceType" gorm:"column:service_type;type:int;comment:服务类型 1:视频 2:图文 3:数据报表 4:账号数 5:可用时长 6:竞品数"`
ValueAddServiceLang []ValueAddServiceLang `gorm:"foreignKey:UUID;references:UUID"`
BundleToValueAddService []BundleToValueAddService `gorm:"foreignKey:ValueUid;references:UUID"`
CreatedAt int64 `gorm:"column:created_at;autoCreateTime"`
@ -48,7 +48,7 @@ type ValueAddServiceLang struct {
Id int32 `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT" json:"id"`
UUID string `json:"uuid" gorm:"column:uuid;type:varchar(1024);comment:增值套餐UUID"`
ServiceName string `json:"serviceName" gorm:"column:service_name;type:varchar(1024);comment:增值服务名称"`
ServiceType int32 `json:"serviceType" gorm:"column:service_type;type:int;comment:服务类型 1:视频 2:图文 3:数据报表 4:账号数 5:可用时长"`
ServiceType int32 `json:"serviceType" gorm:"column:service_type;type:int;comment:服务类型 1:视频 2:图文 3:数据报表 4:账号数 5:可用时长 6:竞品数"`
PriceMode int32 `json:"priceMode" gorm:"column:price_mode;type:int;comment:套餐价格类型 1:单价 2:总价"`
OriginalPrice float32 `json:"originalPrice" gorm:"column:original_price;type:decimal(12,2);comment:原单价"`
TotalPrice float32 `json:"totalPrice" gorm:"column:total_price;type:decimal(12,2);comment:增值服务总价"` //总价模式不合理,该字段暂时无用

47
internal/testutil/db.go Normal file
View File

@ -0,0 +1,47 @@
package testutil
import (
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"micro-bundle/pkg/app"
"micro-bundle/pkg/db"
)
// SetupTaskBenchDB 创建一个 sqlmock 驱动的 *gorm.DB,挂到 app.ModuleClients.TaskBenchDB。
// 返回的 sqlmock 用于在测试中声明期望 SQL,cleanup 自动还原全局单例。
//
// 使用 QueryMatcherRegexp 匹配,SQL 文本作为正则匹配(支持 .* 等),
// 避免对 gorm 生成 SQL 的细节(字段顺序、占位符数量)过度耦合。
func SetupTaskBenchDB(t *testing.T) sqlmock.Sqlmock {
t.Helper()
mockDB, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherRegexp))
require.NoError(t, err)
gormDB, err := gorm.Open(mysql.New(mysql.Config{
Conn: mockDB,
SkipInitializeWithVersion: true,
}), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
DisableForeignKeyConstraintWhenMigrating: true,
})
require.NoError(t, err)
original := app.ModuleClients
app.ModuleClients = &app.App{
Lg: zap.NewNop(),
TaskBenchDB: &db.TaskBenchDB{DB: gormDB},
}
t.Cleanup(func() { app.ModuleClients = original })
return mock
}

View File

@ -4,6 +4,13 @@ package bundle;
option go_package = "./bundle";
//
enum BundleTaskType {
BUNDLE_TASK_TYPE_UNKNOWN = 0;
BUNDLE_TASK_TYPE_BASIC = 1; //
BUNDLE_TASK_TYPE_PAY_LATER = 2; //
}
import "pb/descriptor.proto";
import "pb/validator.proto";
@ -35,6 +42,8 @@ service Bundle {
rpc OnlyAddValueListByOrderNo(OnlyAddValueListByOrderNoRequest) returns (OnlyAddValueListByOrderNoResp) {} // orderNo只查增值服务
rpc ReSignTheContract(ReSignTheContractRequest) returns (CommonResponse) {}
rpc GetInEffectOrderRecord(GetInEffectOrderRecordRequest) returns (OrderRecord) {} //
rpc CheckOrderEligibility(CheckOrderEligibilityRequest) returns (CheckOrderEligibilityResponse) {} // ()
rpc MarkOverdueOrders(MarkOverdueOrdersRequest) returns (MarkOverdueOrdersResponse) {} // ()
//
rpc CreateValueAddBundle(CreateValueAddBundleRequest) returns (CreateValueAddBundleResponse) {}
@ -57,6 +66,7 @@ service Bundle {
rpc BundleExtendRecordsList(BundleExtendRecordsListRequest) returns (BundleExtendRecordsListResponse) {} //
rpc GetBundleBalanceList(GetBundleBalanceListReq) returns (GetBundleBalanceListResp) {} //
rpc GetBundleBalanceByUserId(GetBundleBalanceByUserIdReq) returns (GetBundleBalanceByUserIdResp) {} //
rpc GetBundleOrderListByUserId(GetBundleOrderListByUserIdReq) returns (GetBundleOrderListByUserIdResp) {} // ID查询订单列表
rpc GetBundleBalanceByOrderUUID(GetBundleBalanceByOrderUUIDReq) returns (GetBundleBalanceByOrderUUIDResp) {}
rpc CreateBundleBalance(CreateBundleBalanceReq) returns (CreateBundleBalanceResp) {} //
rpc AddBundleBalance(AddBundleBalanceReq) returns (AddBundleBalanceResp) {} //
@ -266,6 +276,10 @@ message OrderCreateRecord{
uint64 inviterId = 23; // ID
uint64 purchaseType = 24; // 1 2
string renewalOrderUUID = 25; // UUID
int32 orderMode = 26 [json_name = "orderMode"]; // 1: 2:
string dueTime = 27 [json_name = "dueTime"]; // ()
int32 payLaterStatus = 28 [json_name = "payLaterStatus"]; // 0: 1: 2: 3:
int32 contractTplType = 29 [json_name = "contractTplType"]; // 1: 2: 3:
}
message OrderCreateAddRecord{
int32 serviceType = 1 [json_name = "serviceType"];
@ -281,6 +295,10 @@ message OrderCreateAddRecord{
int32 quotaType = 11 [json_name = "quotaType"];
int32 quotaValue = 12 [json_name = "quotaValue"];
bool isExpired = 13 [json_name = "isExpired"];
int32 orderMode = 14 [json_name = "orderMode"]; // 1: 2:(2)
string dueTime = 15 [json_name = "dueTime"]; //
int32 payLaterStatus = 16 [json_name = "payLaterStatus"]; // 0: 1: 2: 3:
int32 contractTplType = 17 [json_name = "contractTplType"]; // 3:
}
message OrderRecordsRequestV2{
string customerName = 1;
@ -300,6 +318,8 @@ message OrderRecordsRequestV2{
string bundlePayStart = 15;
string bundlePayEnd = 16;
uint64 purchaseType = 17;
int32 orderMode = 18 [json_name = "orderMode"]; // 1: 2:
int32 payLaterStatus = 19 [json_name = "payLaterStatus"]; // 0: 1: 2: 3:
}
message OrderRecordsResponseV2{
repeated OrderBundleRecordInfo bundleInfo = 1;
@ -324,6 +344,10 @@ message OrderBundleRecordInfo{
string inviterName = 14;
uint64 purchaseType = 15;
string renewalOrderUUID = 16;
int32 orderMode = 17 [json_name = "orderMode"]; // 1: 2:
string dueTime = 18 [json_name = "dueTime"]; //
int32 payLaterStatus = 19 [json_name = "payLaterStatus"]; // 0: 1: 2: 3:
int32 contractTplType = 20 [json_name = "contractTplType"]; // 1: 2:
}
message OrderAddBundleRecordInfo{
string orderAddNo = 1;
@ -339,6 +363,10 @@ message OrderAddBundleRecordInfo{
string CheckoutSessionId = 11;
string CustomerID = 12;
int32 videoNum = 13;
int32 orderMode = 14 [json_name = "orderMode"]; // 1: 2:2
string dueTime = 15 [json_name = "dueTime"]; //
int32 payLaterStatus = 16 [json_name = "payLaterStatus"]; // 0: 1: 2: 3:
int32 contractTplType = 17 [json_name = "contractTplType"]; // 3:
}
message PackagePriceAndTimeResponse{
float price = 1 [json_name = "price"];
@ -373,6 +401,8 @@ message BundleProfile {
repeated BundleProfileLang bundleProfileLang = 19 [json_name = "bundleProfileLang"];
int32 imgOption = 20 [json_name = "imgOption"];
string fontColor = 21 [json_name = "fontColor"];
int64 bundleType = 22 [json_name = "bundleType"];
int64 payLaterTime = 23 [json_name = "payLaterTime"]; //
}
message BundleProfileLang {
string uuid = 1 [json_name = "uuid"];
@ -392,7 +422,9 @@ message BundleProfileLang {
string bgImg2 = 15 [json_name = "bgImg2"];
int64 shelfStatus = 16 [json_name = "shelfStatus"]; // 1 2
int32 imgOption = 17 [json_name = "imgOption"];
repeated ServiceLangInfo serviceLangInfo = 18 [json_name = "serviceLangInfo"];//
int64 bundleType = 18 [json_name = "bundleType"];
int64 payLaterTime = 19 [json_name = "payLaterTime"]; //
repeated ServiceLangInfo serviceLangInfo = 20 [json_name = "serviceLangInfo"];//
//repeated ValueAddServiceLang valueAddServiceLang = 12 [json_name = "ValueAddServiceLang"];
}
@ -441,6 +473,7 @@ message BundleListRequest {
string name = 3 [json_name = "name"];
string content = 4 [json_name = "content"];
string language = 5 [json_name = "language"];
int32 bundleType = 6 [json_name = "bundleType"];
}
message BundleListResponse {
@ -507,6 +540,32 @@ message OrderRecord {
int32 reSignature = 40 [json_name = "reSignature"];
uint64 purchaseType = 41 [json_name = "purchaseType"]; // 1 2
string renewalOrderUUID = 42 [json_name = "renewalOrderUUID"];
int32 orderMode = 43 [json_name = "orderMode"]; // 1: 2:
string dueTime = 44 [json_name = "dueTime"]; //
int32 payLaterStatus = 45 [json_name = "payLaterStatus"]; // 0: 1: 2: 3:
int32 contractTplType = 46 [json_name = "contractTplType"]; //
}
//
message CheckOrderEligibilityRequest {
string customerID = 1 [json_name = "customerID"];
int32 orderKind = 2 [json_name = "orderKind"]; // 1: 2:
int32 orderMode = 3 [json_name = "orderMode"]; // 1: 2:
}
message CheckOrderEligibilityResponse {
bool allow = 1 [json_name = "allow"];
string reason = 2 [json_name = "reason"]; // :PAYLATER_BUNDLE_OVERDUE / BUNDLE_ACTIVE_EXISTS
string msg = 3 [json_name = "msg"];
}
//
message MarkOverdueOrdersRequest {
string now = 1 [json_name = "now"]; // , now()
}
message MarkOverdueOrdersResponse {
int32 bundleOverdueCount = 1 [json_name = "bundleOverdueCount"];
int32 valueAddExpiredCount = 2 [json_name = "valueAddExpiredCount"];
string msg = 3 [json_name = "msg"];
}
message AddInfo{
string orderNo = 1 [json_name = "orderNo"];
@ -531,6 +590,10 @@ message OrderAddRecord{
string expirationDate = 11 [json_name = "expirationDate"];
string snapshot = 38 [json_name = "snapshot"];
string orderUUID = 39 [json_name = "orderUUID"];
int32 orderMode = 40 [json_name = "orderMode"]; // 1: 2:2
string dueTime = 41 [json_name = "dueTime"]; //
int32 payLaterStatus = 42 [json_name = "payLaterStatus"]; // 0: 1: 2: 3:
int32 contractTplType = 43 [json_name = "contractTplType"]; // 3:
}
message AddPriceOptionsInfo {
int32 id = 1 [json_name = "id"];
@ -564,6 +627,8 @@ message OrderRecordsRequest {
int32 financialConfirmation = 15 [json_name = "financialConfirmation"];
string telNum = 16 [json_name = "telNum"];
string purchaseType = 17 [json_name = "purchaseType"]; // 1 2
int32 orderMode = 18 [json_name = "orderMode"]; // 1: 2:
int32 payLaterStatus = 19 [json_name = "payLaterStatus"]; // 0: 1: 2: 3:
}
message OrderRecordsResponse {
@ -1171,6 +1236,7 @@ message ToBeComfirmedWorksResp{
message GetBundleBalanceByUserIdReq{
int32 userId = 1;
string orderUUID = 2;
}
message GetBundleBalanceByUserIdResp{
@ -1212,6 +1278,14 @@ message GetBundleBalanceByUserIdResp{
string renewalOrderUuid = 36;
}
message GetBundleOrderListByUserIdReq{
int32 userId = 1;
}
message GetBundleOrderListByUserIdResp{
repeated string orderUuid = 1;
}
message GetBundleBalanceByOrderUUIDReq{
string orderUUID = 1;
}
@ -1320,6 +1394,7 @@ message TaskQueryRequest {
string sortType = 5 [json_name = "sortType"]; // asc/desc
string lastTaskAssignee = 6 [json_name = "lastTaskAssignee"]; //
repeated string subNums = 7 [json_name = "subNums"]; //
BundleTaskType bundleTaskType = 8 [json_name = "bundleTaskType"]; // 0/1: 2:
}
message TaskQueryResponse {
@ -1359,6 +1434,7 @@ message TaskAssignRequest {
int32 assignVideoScriptCount = 12 [json_name = "assignVideoScriptCount"]; //
int32 assignReportCount = 13 [json_name = "assignReportCount"]; //
int32 userID = 14 [json_name = "userID"]; // ID
BundleTaskType bundleTaskType = 15 [json_name = "bundleTaskType"]; //
}
//
@ -1408,6 +1484,7 @@ message EmployeeTaskQueryRequest {
int32 page = 10 [json_name = "page"]; //
int32 pageSize = 11 [json_name = "pageSize"]; //
string taskBatch = 12 [json_name = "taskBatch"]; //
BundleTaskType bundleTaskType = 13 [json_name = "bundleTaskType"]; //
}
message EmployeeTaskQueryResponse {
@ -1444,6 +1521,7 @@ message TaskAssignRecordInfo {
int32 completeDataCount = 23 [json_name = "completeDataCount"]; //
int32 pendingReportCount = 24 [json_name = "pendingReportCount"]; //
int32 completeReportCount = 25 [json_name = "completeReportCount"]; //
BundleTaskType bundleTaskType = 26 [json_name = "bundleTaskType"]; //
}
//
@ -1462,6 +1540,7 @@ message BatchAssignTaskItem {
int32 assignVideoScriptCount = 12 [json_name = "assignVideoScriptCount"]; //
int32 assignReportCount = 13 [json_name = "assignReportCount"]; //
int32 userID = 14 [json_name = "userID"]; // ID
BundleTaskType bundleTaskType = 15 [json_name = "bundleTaskType"]; //
}
message BatchAssignTaskRequest {
@ -1521,6 +1600,7 @@ message TaskAssignRecordsQueryRequest {
string taskBatch = 11 [json_name = "taskBatch"]; //
string sortBy = 12 [json_name = "sortBy"]; //
string sortType = 13 [json_name = "sortType"]; //
BundleTaskType bundleTaskType = 14 [json_name = "bundleTaskType"]; //
}
message ComResponse {
@ -1648,6 +1728,7 @@ message PendingAssignRequest {
repeated string subNums = 1 [json_name = "subNums"];
int32 page = 2 [json_name = "page"];
int32 pageSize = 3 [json_name = "pageSize"];
BundleTaskType bundleTaskType = 4 [json_name = "bundleTaskType"]; // 0/1: 2:
}
//
@ -1733,6 +1814,7 @@ message TaskWorkLogQueryRequest {
string artistName = 6 [json_name = "artistName"];
int32 page = 7 [json_name = "page"];
int32 pageSize = 8 [json_name = "pageSize"];
BundleTaskType bundleTaskType = 9 [json_name = "bundleTaskType"]; //
}
message TaskWorkLogInfo {
@ -1753,6 +1835,7 @@ message TaskWorkLogInfo {
int64 operationTime = 15 [json_name = "taskOperationTime"];
int64 createdAt = 16 [json_name = "taskCreatedAt"];
int64 updatedAt = 17 [json_name = "taskUpdatedAt"];
BundleTaskType bundleTaskType = 18 [json_name = "bundleTaskType"]; //
}
message TaskWorkLogQueryResponse {
@ -1777,6 +1860,7 @@ message CreateTaskWorkLogRequest {
string remark = 11 [json_name = "remark"]; //
string operatorName = 12 [json_name = "operatorName"]; //
string operatorNum = 13 [json_name = "operatorNum"]; //
BundleTaskType bundleTaskType = 14 [json_name = "bundleTaskType"]; //
}
message MetricsBusinessReq{
@ -2174,6 +2258,7 @@ message ContractInfo {
repeated AttachmentItem otherAttachments = 18 [json_name = "otherAttachments"]; // url+fileName
string operator = 19 [json_name = "operator"]; //
string operatorTime = 22 [json_name = "operatorTime"]; //
int32 contractTemplateType = 23 [json_name = "contractTemplateType"]; // 1: 2: 3:
}
//
@ -2202,6 +2287,7 @@ message Contract {
string operatorTime = 23 [json_name = "operatorTime"]; //
repeated ContractPaymentCycle paymentCycles = 24 [json_name = "paymentCycles"]; //
repeated DevelopmentCycle developmentCycles = 25 [json_name = "developmentCycles"]; //
int32 contractTemplateType = 26 [json_name = "contractTemplateType"]; // 1: 2: 3:
}
//
@ -2384,6 +2470,7 @@ message GetLastInvoiceNoResp{
string lastNo = 1;
}
message UpdataInvoiceInfoReq{
string orderNo = 1;
string url = 2;

File diff suppressed because it is too large Load Diff

View File

@ -217,6 +217,18 @@ func (this *OrderRecord) Validate() error {
}
return nil
}
func (this *CheckOrderEligibilityRequest) Validate() error {
return nil
}
func (this *CheckOrderEligibilityResponse) Validate() error {
return nil
}
func (this *MarkOverdueOrdersRequest) Validate() error {
return nil
}
func (this *MarkOverdueOrdersResponse) Validate() error {
return nil
}
func (this *AddInfo) Validate() error {
return nil
}
@ -493,6 +505,12 @@ func (this *GetBundleBalanceByUserIdReq) Validate() error {
func (this *GetBundleBalanceByUserIdResp) Validate() error {
return nil
}
func (this *GetBundleOrderListByUserIdReq) Validate() error {
return nil
}
func (this *GetBundleOrderListByUserIdResp) Validate() error {
return nil
}
func (this *GetBundleBalanceByOrderUUIDReq) Validate() error {
return nil
}

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-triple. DO NOT EDIT.
// versions:
// - protoc-gen-go-triple v1.0.5
// - protoc v5.26.0
// - protoc-gen-go-triple v1.0.8
// - protoc v3.21.1
// source: pb/bundle.proto
package bundle
@ -52,6 +52,8 @@ type BundleClient interface {
OnlyAddValueListByOrderNo(ctx context.Context, in *OnlyAddValueListByOrderNoRequest, opts ...grpc_go.CallOption) (*OnlyAddValueListByOrderNoResp, common.ErrorWithAttachment)
ReSignTheContract(ctx context.Context, in *ReSignTheContractRequest, opts ...grpc_go.CallOption) (*CommonResponse, common.ErrorWithAttachment)
GetInEffectOrderRecord(ctx context.Context, in *GetInEffectOrderRecordRequest, opts ...grpc_go.CallOption) (*OrderRecord, common.ErrorWithAttachment)
CheckOrderEligibility(ctx context.Context, in *CheckOrderEligibilityRequest, opts ...grpc_go.CallOption) (*CheckOrderEligibilityResponse, common.ErrorWithAttachment)
MarkOverdueOrders(ctx context.Context, in *MarkOverdueOrdersRequest, opts ...grpc_go.CallOption) (*MarkOverdueOrdersResponse, common.ErrorWithAttachment)
// 增值套餐
CreateValueAddBundle(ctx context.Context, in *CreateValueAddBundleRequest, opts ...grpc_go.CallOption) (*CreateValueAddBundleResponse, common.ErrorWithAttachment)
ValueAddBundleList(ctx context.Context, in *ValueAddBundleListRequest, opts ...grpc_go.CallOption) (*ValueAddBundleListResponse, common.ErrorWithAttachment)
@ -70,6 +72,7 @@ type BundleClient interface {
BundleExtendRecordsList(ctx context.Context, in *BundleExtendRecordsListRequest, opts ...grpc_go.CallOption) (*BundleExtendRecordsListResponse, common.ErrorWithAttachment)
GetBundleBalanceList(ctx context.Context, in *GetBundleBalanceListReq, opts ...grpc_go.CallOption) (*GetBundleBalanceListResp, common.ErrorWithAttachment)
GetBundleBalanceByUserId(ctx context.Context, in *GetBundleBalanceByUserIdReq, opts ...grpc_go.CallOption) (*GetBundleBalanceByUserIdResp, common.ErrorWithAttachment)
GetBundleOrderListByUserId(ctx context.Context, in *GetBundleOrderListByUserIdReq, opts ...grpc_go.CallOption) (*GetBundleOrderListByUserIdResp, common.ErrorWithAttachment)
GetBundleBalanceByOrderUUID(ctx context.Context, in *GetBundleBalanceByOrderUUIDReq, opts ...grpc_go.CallOption) (*GetBundleBalanceByOrderUUIDResp, common.ErrorWithAttachment)
CreateBundleBalance(ctx context.Context, in *CreateBundleBalanceReq, opts ...grpc_go.CallOption) (*CreateBundleBalanceResp, common.ErrorWithAttachment)
AddBundleBalance(ctx context.Context, in *AddBundleBalanceReq, opts ...grpc_go.CallOption) (*AddBundleBalanceResp, common.ErrorWithAttachment)
@ -184,6 +187,8 @@ type BundleClientImpl struct {
OnlyAddValueListByOrderNo func(ctx context.Context, in *OnlyAddValueListByOrderNoRequest) (*OnlyAddValueListByOrderNoResp, error)
ReSignTheContract func(ctx context.Context, in *ReSignTheContractRequest) (*CommonResponse, error)
GetInEffectOrderRecord func(ctx context.Context, in *GetInEffectOrderRecordRequest) (*OrderRecord, error)
CheckOrderEligibility func(ctx context.Context, in *CheckOrderEligibilityRequest) (*CheckOrderEligibilityResponse, error)
MarkOverdueOrders func(ctx context.Context, in *MarkOverdueOrdersRequest) (*MarkOverdueOrdersResponse, error)
CreateValueAddBundle func(ctx context.Context, in *CreateValueAddBundleRequest) (*CreateValueAddBundleResponse, error)
ValueAddBundleList func(ctx context.Context, in *ValueAddBundleListRequest) (*ValueAddBundleListResponse, error)
ValueAddBundleDetail func(ctx context.Context, in *ValueAddBundleDetailRequest) (*ValueAddBundleDetailResponse, error)
@ -199,6 +204,7 @@ type BundleClientImpl struct {
BundleExtendRecordsList func(ctx context.Context, in *BundleExtendRecordsListRequest) (*BundleExtendRecordsListResponse, error)
GetBundleBalanceList func(ctx context.Context, in *GetBundleBalanceListReq) (*GetBundleBalanceListResp, error)
GetBundleBalanceByUserId func(ctx context.Context, in *GetBundleBalanceByUserIdReq) (*GetBundleBalanceByUserIdResp, error)
GetBundleOrderListByUserId func(ctx context.Context, in *GetBundleOrderListByUserIdReq) (*GetBundleOrderListByUserIdResp, error)
GetBundleBalanceByOrderUUID func(ctx context.Context, in *GetBundleBalanceByOrderUUIDReq) (*GetBundleBalanceByOrderUUIDResp, error)
CreateBundleBalance func(ctx context.Context, in *CreateBundleBalanceReq) (*CreateBundleBalanceResp, error)
AddBundleBalance func(ctx context.Context, in *AddBundleBalanceReq) (*AddBundleBalanceResp, error)
@ -430,6 +436,18 @@ func (c *bundleClient) GetInEffectOrderRecord(ctx context.Context, in *GetInEffe
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GetInEffectOrderRecord", in, out)
}
func (c *bundleClient) CheckOrderEligibility(ctx context.Context, in *CheckOrderEligibilityRequest, opts ...grpc_go.CallOption) (*CheckOrderEligibilityResponse, common.ErrorWithAttachment) {
out := new(CheckOrderEligibilityResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/CheckOrderEligibility", in, out)
}
func (c *bundleClient) MarkOverdueOrders(ctx context.Context, in *MarkOverdueOrdersRequest, opts ...grpc_go.CallOption) (*MarkOverdueOrdersResponse, common.ErrorWithAttachment) {
out := new(MarkOverdueOrdersResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/MarkOverdueOrders", in, out)
}
func (c *bundleClient) CreateValueAddBundle(ctx context.Context, in *CreateValueAddBundleRequest, opts ...grpc_go.CallOption) (*CreateValueAddBundleResponse, common.ErrorWithAttachment) {
out := new(CreateValueAddBundleResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
@ -520,6 +538,12 @@ func (c *bundleClient) GetBundleBalanceByUserId(ctx context.Context, in *GetBund
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GetBundleBalanceByUserId", in, out)
}
func (c *bundleClient) GetBundleOrderListByUserId(ctx context.Context, in *GetBundleOrderListByUserIdReq, opts ...grpc_go.CallOption) (*GetBundleOrderListByUserIdResp, common.ErrorWithAttachment) {
out := new(GetBundleOrderListByUserIdResp)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GetBundleOrderListByUserId", in, out)
}
func (c *bundleClient) GetBundleBalanceByOrderUUID(ctx context.Context, in *GetBundleBalanceByOrderUUIDReq, opts ...grpc_go.CallOption) (*GetBundleBalanceByOrderUUIDResp, common.ErrorWithAttachment) {
out := new(GetBundleBalanceByOrderUUIDResp)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
@ -986,6 +1010,8 @@ type BundleServer interface {
OnlyAddValueListByOrderNo(context.Context, *OnlyAddValueListByOrderNoRequest) (*OnlyAddValueListByOrderNoResp, error)
ReSignTheContract(context.Context, *ReSignTheContractRequest) (*CommonResponse, error)
GetInEffectOrderRecord(context.Context, *GetInEffectOrderRecordRequest) (*OrderRecord, error)
CheckOrderEligibility(context.Context, *CheckOrderEligibilityRequest) (*CheckOrderEligibilityResponse, error)
MarkOverdueOrders(context.Context, *MarkOverdueOrdersRequest) (*MarkOverdueOrdersResponse, error)
// 增值套餐
CreateValueAddBundle(context.Context, *CreateValueAddBundleRequest) (*CreateValueAddBundleResponse, error)
ValueAddBundleList(context.Context, *ValueAddBundleListRequest) (*ValueAddBundleListResponse, error)
@ -1004,6 +1030,7 @@ type BundleServer interface {
BundleExtendRecordsList(context.Context, *BundleExtendRecordsListRequest) (*BundleExtendRecordsListResponse, error)
GetBundleBalanceList(context.Context, *GetBundleBalanceListReq) (*GetBundleBalanceListResp, error)
GetBundleBalanceByUserId(context.Context, *GetBundleBalanceByUserIdReq) (*GetBundleBalanceByUserIdResp, error)
GetBundleOrderListByUserId(context.Context, *GetBundleOrderListByUserIdReq) (*GetBundleOrderListByUserIdResp, error)
GetBundleBalanceByOrderUUID(context.Context, *GetBundleBalanceByOrderUUIDReq) (*GetBundleBalanceByOrderUUIDResp, error)
CreateBundleBalance(context.Context, *CreateBundleBalanceReq) (*CreateBundleBalanceResp, error)
AddBundleBalance(context.Context, *AddBundleBalanceReq) (*AddBundleBalanceResp, error)
@ -1167,6 +1194,12 @@ func (UnimplementedBundleServer) ReSignTheContract(context.Context, *ReSignTheCo
func (UnimplementedBundleServer) GetInEffectOrderRecord(context.Context, *GetInEffectOrderRecordRequest) (*OrderRecord, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetInEffectOrderRecord not implemented")
}
func (UnimplementedBundleServer) CheckOrderEligibility(context.Context, *CheckOrderEligibilityRequest) (*CheckOrderEligibilityResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CheckOrderEligibility not implemented")
}
func (UnimplementedBundleServer) MarkOverdueOrders(context.Context, *MarkOverdueOrdersRequest) (*MarkOverdueOrdersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method MarkOverdueOrders not implemented")
}
func (UnimplementedBundleServer) CreateValueAddBundle(context.Context, *CreateValueAddBundleRequest) (*CreateValueAddBundleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateValueAddBundle not implemented")
}
@ -1212,6 +1245,9 @@ func (UnimplementedBundleServer) GetBundleBalanceList(context.Context, *GetBundl
func (UnimplementedBundleServer) GetBundleBalanceByUserId(context.Context, *GetBundleBalanceByUserIdReq) (*GetBundleBalanceByUserIdResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBundleBalanceByUserId not implemented")
}
func (UnimplementedBundleServer) GetBundleOrderListByUserId(context.Context, *GetBundleOrderListByUserIdReq) (*GetBundleOrderListByUserIdResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBundleOrderListByUserId not implemented")
}
func (UnimplementedBundleServer) GetBundleBalanceByOrderUUID(context.Context, *GetBundleBalanceByOrderUUIDReq) (*GetBundleBalanceByOrderUUIDResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBundleBalanceByOrderUUID not implemented")
}
@ -2155,6 +2191,64 @@ func _Bundle_GetInEffectOrderRecord_Handler(srv interface{}, ctx context.Context
return interceptor(ctx, in, info, handler)
}
func _Bundle_CheckOrderEligibility_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(CheckOrderEligibilityRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("CheckOrderEligibility", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Bundle_MarkOverdueOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(MarkOverdueOrdersRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("MarkOverdueOrders", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Bundle_CreateValueAddBundle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateValueAddBundleRequest)
if err := dec(in); err != nil {
@ -2590,6 +2684,35 @@ func _Bundle_GetBundleBalanceByUserId_Handler(srv interface{}, ctx context.Conte
return interceptor(ctx, in, info, handler)
}
func _Bundle_GetBundleOrderListByUserId_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(GetBundleOrderListByUserIdReq)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("GetBundleOrderListByUserId", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Bundle_GetBundleBalanceByOrderUUID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(GetBundleBalanceByOrderUUIDReq)
if err := dec(in); err != nil {
@ -4810,6 +4933,14 @@ var Bundle_ServiceDesc = grpc_go.ServiceDesc{
MethodName: "GetInEffectOrderRecord",
Handler: _Bundle_GetInEffectOrderRecord_Handler,
},
{
MethodName: "CheckOrderEligibility",
Handler: _Bundle_CheckOrderEligibility_Handler,
},
{
MethodName: "MarkOverdueOrders",
Handler: _Bundle_MarkOverdueOrders_Handler,
},
{
MethodName: "CreateValueAddBundle",
Handler: _Bundle_CreateValueAddBundle_Handler,
@ -4870,6 +5001,10 @@ var Bundle_ServiceDesc = grpc_go.ServiceDesc{
MethodName: "GetBundleBalanceByUserId",
Handler: _Bundle_GetBundleBalanceByUserId_Handler,
},
{
MethodName: "GetBundleOrderListByUserId",
Handler: _Bundle_GetBundleOrderListByUserId_Handler,
},
{
MethodName: "GetBundleBalanceByOrderUUID",
Handler: _Bundle_GetBundleBalanceByOrderUUID_Handler,

View File

@ -38,7 +38,7 @@ func loadMysqlConn(conn string) *gorm.DB {
if err != nil {
panic(err)
}
fmt.Println("[BundleDB] 数据库连接成功, 连接字符串: %s", conn)
fmt.Printf("[BundleDB] 数据库连接成功, 连接字符串: %s\n", conn)
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(20) //设置连接池,空闲
sqlDB.SetMaxOpenConns(100) //打开
@ -95,6 +95,24 @@ func loadMysqlConn(conn string) *gorm.DB {
fmt.Println(err)
}
}
// 先用后付字段迁移
for _, col := range []string{"order_mode", "due_time", "pay_later_status", "contract_tpl_type"} {
if !db.Migrator().HasColumn(&model.BundleOrderRecords{}, col) {
if err := db.Migrator().AddColumn(&model.BundleOrderRecords{}, col); err != nil {
fmt.Println("[migrate BundleOrderRecords]", col, err)
}
}
if !db.Migrator().HasColumn(&model.BundleOrderValueAdd{}, col) {
if err := db.Migrator().AddColumn(&model.BundleOrderValueAdd{}, col); err != nil {
fmt.Println("[migrate BundleOrderValueAdd]", col, err)
}
}
}
if !db.Migrator().HasColumn(&model.Contract{}, "contract_template_type") {
if err := db.Migrator().AddColumn(&model.Contract{}, "contract_template_type"); err != nil {
fmt.Println("[migrate Contract] contract_template_type", err)
}
}
if err != nil {
// return nil
panic(err)

View File

@ -119,6 +119,7 @@ const (
DataReportService = 3 //数据报表
AccountService = 4 //账号数
AvailableTimeService = 5 //可用时长
CompetitiveService = 6 //竞品数
)
// 套餐状态