ctr_contract_advance.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "strconv"
  6. "sync"
  7. "time"
  8. contractdao "dashoo.cn/opms_parent/app/dao/contract"
  9. opsdevdao "dashoo.cn/opms_parent/app/dao/opsdev"
  10. projdao "dashoo.cn/opms_parent/app/dao/proj"
  11. contractmodel "dashoo.cn/opms_parent/app/model/contract"
  12. workflowmodel "dashoo.cn/opms_parent/app/model/workflow"
  13. "dashoo.cn/opms_parent/app/service"
  14. "dashoo.cn/opms_libary/micro_srv"
  15. "dashoo.cn/opms_libary/myerrors"
  16. "dashoo.cn/opms_libary/plugin/dingtalk/message"
  17. "github.com/gogf/gf/database/gdb"
  18. "github.com/gogf/gf/frame/g"
  19. "github.com/gogf/gf/os/gtime"
  20. "github.com/gogf/gf/util/gconv"
  21. )
  22. // ctrContractAdvanceService 合同提前执行申请服务
  23. type ctrContractAdvanceService struct {
  24. *service.ContextService
  25. Dao *contractdao.CtrContractAdvanceDao
  26. }
  27. var ctrContractAdvanceMtx sync.Mutex
  28. // NewCtrContractAdvanceService 初始化服务
  29. func NewCtrContractAdvanceService(ctx context.Context) (*ctrContractAdvanceService, error) {
  30. svc := &ctrContractAdvanceService{}
  31. var err error
  32. if svc.ContextService, err = svc.Init(ctx); err != nil {
  33. return nil, err
  34. }
  35. svc.Dao = contractdao.NewCtrContractAdvanceDao(svc.Tenant)
  36. return svc, nil
  37. }
  38. // GetList 分页查询
  39. func (s *ctrContractAdvanceService) GetList(ctx context.Context, req *contractmodel.CtrContractAdvanceSearchReq) (total int, list []*contractmodel.CtrContractAdvanceRsp, err error) {
  40. db := s.Dao.As("a")
  41. // 拼接查询条件
  42. if req.SearchText != "" {
  43. likestr := fmt.Sprintf("%%%s%%", req.SearchText)
  44. db = db.Where("(a.advance_code LIKE ? OR a.advance_name LIKE ? OR a.cust_name LIKE ? OR a.nbo_name LIKE ?)",
  45. likestr, likestr, likestr, likestr)
  46. }
  47. if req.AdvanceCode != "" {
  48. likestr := fmt.Sprintf("%%%s%%", req.AdvanceCode)
  49. db = db.Where("a.advance_code LIKE ?", likestr)
  50. }
  51. if req.AdvanceName != "" {
  52. likestr := fmt.Sprintf("%%%s%%", req.AdvanceName)
  53. db = db.Where("a.advance_name LIKE ?", likestr)
  54. }
  55. if req.CustId != 0 {
  56. db = db.Where("a.cust_id = ?", req.CustId)
  57. }
  58. if req.CustName != "" {
  59. likestr := fmt.Sprintf("%%%s%%", req.CustName)
  60. db = db.Where("a.cust_name LIKE ?", likestr)
  61. }
  62. if req.NboId != 0 {
  63. db = db.Where("a.nbo_id = ?", req.NboId)
  64. }
  65. if req.ApproStatus != "" {
  66. db = db.Where("a.appro_status = ?", req.ApproStatus)
  67. }
  68. if req.ProductLine != "" {
  69. db = db.Where("a.product_line = ?", req.ProductLine)
  70. }
  71. if req.InchargeId != 0 {
  72. db = db.Where("a.incharge_id = ?", req.InchargeId)
  73. }
  74. if req.ContractId != 0 {
  75. db = db.Where("a.contract_id = ?", req.ContractId)
  76. }
  77. // 统计总行数
  78. total, err = db.Count()
  79. if err != nil {
  80. g.Log().Error(err)
  81. return 0, nil, myerrors.DbError("获取提前执行申请总行数失败")
  82. }
  83. // 分页、排序、查询
  84. var entityList []*contractmodel.CtrContractAdvance
  85. err = db.Page(req.GetPage()).OrderDesc("a.id").Scan(&entityList)
  86. if err != nil {
  87. g.Log().Error(err)
  88. return 0, nil, myerrors.DbError("查询提前执行申请列表失败")
  89. }
  90. // 转换为响应结构体
  91. if err = gconv.Structs(entityList, &list); err != nil {
  92. g.Log().Error(err)
  93. return 0, nil, myerrors.DbError("数据转换失败")
  94. }
  95. return total, list, nil
  96. }
  97. // GetById 根据ID查询单条
  98. func (s *ctrContractAdvanceService) GetById(id int) (*contractmodel.CtrContractAdvanceRsp, error) {
  99. entity, err := s.Dao.WherePri(id).One()
  100. if err != nil {
  101. g.Log().Error(err)
  102. return nil, myerrors.DbError("查询提前执行申请数据失败")
  103. }
  104. if entity == nil {
  105. return nil, myerrors.TipsError("提前执行申请数据不存在")
  106. }
  107. var rsp contractmodel.CtrContractAdvanceRsp
  108. if err := gconv.Struct(entity, &rsp); err != nil {
  109. g.Log().Error(err)
  110. return nil, myerrors.DbError("数据转换失败")
  111. }
  112. return &rsp, nil
  113. }
  114. // Create 新增
  115. func (s *ctrContractAdvanceService) Create(req *contractmodel.CtrContractAdvanceAddReq) error {
  116. // 生成申请编号: TQ + 年月日 + 4位序号
  117. advanceCode := s.generateAdvanceCode()
  118. data := gconv.Map(req)
  119. data["advanceCode"] = advanceCode
  120. data["approStatus"] = contractmodel.AdvanceStatusDraft
  121. data["tenantId"] = s.Tenant
  122. // 补齐审计字段
  123. service.SetCreatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  124. // 获取项目信息填充客户信息
  125. if req.NboId != 0 {
  126. projBusinessDao := projdao.NewProjBusinessDao(s.Tenant)
  127. proj, err := projBusinessDao.Where("id = ?", req.NboId).One()
  128. if err != nil {
  129. return myerrors.DbError("查询项目信息失败")
  130. }
  131. if proj != nil {
  132. data["custId"] = proj.CustId
  133. data["custName"] = proj.CustName
  134. data["nboName"] = proj.NboName
  135. data["productLine"] = proj.ProductLine
  136. data["isBig"] = proj.IsBig
  137. data["custProvinceId"] = proj.CustProvinceId
  138. data["custProvince"] = proj.CustProvince
  139. data["custCityId"] = proj.CustCityId
  140. data["custCity"] = proj.CustCity
  141. }
  142. }
  143. // 事务控制
  144. return s.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  145. _, err := s.Dao.TX(tx).Insert(data)
  146. if err != nil {
  147. g.Log().Error(err)
  148. return myerrors.DbError("新增提前执行申请失败")
  149. }
  150. // 写业务动态日志
  151. s.CreateDynamic(s.Tenant, 0, "创建提前执行申请",
  152. fmt.Sprintf("创建提前执行申请:%s", advanceCode),
  153. s.GetCxtUserId(), s.GetCxtUserName())
  154. return nil
  155. })
  156. }
  157. // UpdateById 根据ID更新
  158. func (s *ctrContractAdvanceService) UpdateById(req *contractmodel.CtrContractAdvanceUpdateReq) error {
  159. // 校验数据是否存在
  160. entity, err := s.Dao.WherePri(req.Id).One()
  161. if err != nil {
  162. g.Log().Error(err)
  163. return myerrors.DbError("查询提前执行申请数据失败")
  164. }
  165. if entity == nil {
  166. return myerrors.TipsError("提前执行申请数据不存在")
  167. }
  168. // 校验状态:只有待提交(10)或审核拒绝(40)状态可以编辑
  169. if entity.ApproStatus != contractmodel.AdvanceStatusDraft &&
  170. entity.ApproStatus != contractmodel.AdvanceStatusRejected {
  171. return myerrors.TipsError("当前状态不可编辑")
  172. }
  173. data := gconv.Map(req)
  174. delete(data, "id") // 移除ID字段
  175. // 获取项目信息更新客户信息
  176. if req.NboId != 0 && req.NboId != entity.NboId {
  177. projBusinessDao := projdao.NewProjBusinessDao(s.Tenant)
  178. proj, err := projBusinessDao.Where("id = ?", req.NboId).One()
  179. if err != nil {
  180. return myerrors.DbError("查询项目信息失败")
  181. }
  182. if proj != nil {
  183. data["custId"] = proj.CustId
  184. data["custName"] = proj.CustName
  185. data["nboName"] = proj.NboName
  186. data["productLine"] = proj.ProductLine
  187. data["isBig"] = proj.IsBig
  188. data["custProvinceId"] = proj.CustProvinceId
  189. data["custProvince"] = proj.CustProvince
  190. data["custCityId"] = proj.CustCityId
  191. data["custCity"] = proj.CustCity
  192. }
  193. }
  194. // 补齐审计字段
  195. service.SetUpdatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  196. // 更新操作,排除不可改字段
  197. _, err = s.Dao.FieldsEx(service.UpdateFieldEx...).WherePri(req.Id).Data(data).Update()
  198. if err != nil {
  199. g.Log().Error(err)
  200. return myerrors.DbError("更新提前执行申请失败")
  201. }
  202. // 写业务动态日志
  203. s.CreateDynamic(s.Tenant, req.Id, "编辑提前执行申请",
  204. fmt.Sprintf("编辑提前执行申请:%s", entity.AdvanceCode),
  205. s.GetCxtUserId(), s.GetCxtUserName())
  206. return nil
  207. }
  208. // DeleteById 根据ID单条删除
  209. func (s *ctrContractAdvanceService) DeleteById(id int) error {
  210. entity, err := s.Dao.WherePri(id).One()
  211. if err != nil {
  212. g.Log().Error(err)
  213. return myerrors.DbError("查询提前执行申请数据失败")
  214. }
  215. if entity == nil {
  216. return myerrors.TipsError("提前执行申请数据不存在")
  217. }
  218. // 校验状态:只有待提交(10)或审核拒绝(40)状态可以删除
  219. if entity.ApproStatus != contractmodel.AdvanceStatusDraft &&
  220. entity.ApproStatus != contractmodel.AdvanceStatusRejected {
  221. return myerrors.TipsError("当前状态不可删除")
  222. }
  223. _, err = s.Dao.WherePri(id).Delete()
  224. if err != nil {
  225. g.Log().Error(err)
  226. return myerrors.DbError("删除提前执行申请失败")
  227. }
  228. // 写业务动态日志
  229. s.CreateDynamic(s.Tenant, id, "删除提前执行申请",
  230. fmt.Sprintf("删除提前执行申请:%s", entity.AdvanceCode),
  231. s.GetCxtUserId(), s.GetCxtUserName())
  232. return nil
  233. }
  234. // DeleteByIds 根据ID批量删除
  235. func (s *ctrContractAdvanceService) DeleteByIds(ids []int64) error {
  236. if len(ids) == 0 {
  237. return myerrors.TipsError("请选择需要删除的数据")
  238. }
  239. // 查询所有记录校验状态
  240. var entities []*contractmodel.CtrContractAdvance
  241. err := s.Dao.WhereIn(s.Dao.Columns.Id, ids).Scan(&entities)
  242. if err != nil {
  243. g.Log().Error(err)
  244. return myerrors.DbError("查询提前执行申请数据失败")
  245. }
  246. for _, entity := range entities {
  247. if entity == nil {
  248. continue
  249. }
  250. if entity.ApproStatus != contractmodel.AdvanceStatusDraft &&
  251. entity.ApproStatus != contractmodel.AdvanceStatusRejected {
  252. return myerrors.TipsError(fmt.Sprintf("申请[%s]当前状态不可删除", entity.AdvanceCode))
  253. }
  254. }
  255. _, err = s.Dao.WhereIn(s.Dao.Columns.Id, ids).Delete()
  256. if err != nil {
  257. g.Log().Error(err)
  258. return myerrors.DbError("批量删除提前执行申请失败")
  259. }
  260. // 写业务动态日志
  261. s.CreateDynamic(s.Tenant, 0, "批量删除提前执行申请",
  262. fmt.Sprintf("批量删除提前执行申请,数量:%d", len(ids)),
  263. s.GetCxtUserId(), s.GetCxtUserName())
  264. return nil
  265. }
  266. // Commit 提交审批
  267. func (s *ctrContractAdvanceService) Commit(req *contractmodel.CtrContractAdvanceCommitReq) error {
  268. entity, err := s.Dao.WherePri(req.Id).One()
  269. if err != nil {
  270. g.Log().Error(err)
  271. return myerrors.DbError("查询提前执行申请数据失败")
  272. }
  273. if entity == nil {
  274. return myerrors.TipsError("提前执行申请数据不存在")
  275. }
  276. // 校验状态:只有待提交(10)或审核拒绝(40)状态可以提交
  277. if entity.ApproStatus != contractmodel.AdvanceStatusDraft &&
  278. entity.ApproStatus != contractmodel.AdvanceStatusRejected {
  279. return myerrors.TipsError("当前状态不可提交审批")
  280. }
  281. // 模拟审批:不调用钉钉接口,直接更新状态为审核中
  282. // 如需启用钉钉审批,取消下面注释并添加必要的 import
  283. /*
  284. workflowService, err := workflowsrv.NewFlowService(s.Ctx)
  285. if err != nil {
  286. return err
  287. }
  288. bizCode := strconv.Itoa(entity.Id)
  289. processCode := "PROC-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
  290. _, err = workflowService.StartProcessInstance(bizCode, workflowmodel.ContractAdvance, "", &workflow.StartProcessInstanceRequest{
  291. ProcessCode: &processCode,
  292. FormComponentValues: []*workflow.StartProcessInstanceRequestFormComponentValues{
  293. {
  294. Name: utils.String("申请编号"),
  295. Value: utils.String(entity.AdvanceCode),
  296. },
  297. {
  298. Name: utils.String("项目名称"),
  299. Value: utils.String(entity.AdvanceName),
  300. },
  301. {
  302. Name: utils.String("客户名称"),
  303. Value: utils.String(entity.CustName),
  304. },
  305. {
  306. Name: utils.String("预估金额"),
  307. Value: utils.String(strconv.FormatFloat(entity.EstimateAmount, 'f', 2, 64)),
  308. },
  309. {
  310. Name: utils.String("提前执行原因"),
  311. Value: utils.String(entity.AdvanceReason),
  312. },
  313. },
  314. })
  315. if err != nil {
  316. return err
  317. }
  318. */
  319. // 更新状态为审核中
  320. _, err = s.Dao.WherePri(req.Id).Data(g.Map{
  321. "appro_status": contractmodel.AdvanceStatusApproving,
  322. }).Update()
  323. if err != nil {
  324. g.Log().Error(err)
  325. return myerrors.DbError("更新审批状态失败")
  326. }
  327. // 写业务动态日志
  328. s.CreateDynamic(s.Tenant, req.Id, "提交审批",
  329. fmt.Sprintf("提交提前执行申请审批:%s", entity.AdvanceCode),
  330. s.GetCxtUserId(), s.GetCxtUserName())
  331. return nil
  332. }
  333. // Approve 审批通过
  334. func (s *ctrContractAdvanceService) Approve(req *contractmodel.IdRequiredReq) error {
  335. entity, err := s.Dao.WherePri(req.Id).One()
  336. if err != nil {
  337. g.Log().Error(err)
  338. return myerrors.DbError("查询提前执行申请数据失败")
  339. }
  340. if entity == nil {
  341. return myerrors.TipsError("提前执行申请数据不存在")
  342. }
  343. // 校验状态:只有审核中(20)状态可以通过
  344. if entity.ApproStatus != contractmodel.AdvanceStatusApproving {
  345. return myerrors.TipsError("当前状态不可审批通过")
  346. }
  347. return s.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  348. // 更新状态为审核通过
  349. _, err = s.Dao.TX(tx).WherePri(req.Id).Data(g.Map{
  350. "appro_status": contractmodel.AdvanceStatusApproved,
  351. }).Update()
  352. if err != nil {
  353. g.Log().Error(err)
  354. return myerrors.DbError("更新审批状态失败")
  355. }
  356. // 创建交付项目
  357. err = CreateProjectByAdvance(tx, s.Tenant, entity, s.GetCxtUserId(), s.GetCxtUserName())
  358. if err != nil {
  359. g.Log().Error(err)
  360. return myerrors.DbError("创建交付项目失败")
  361. }
  362. return nil
  363. })
  364. }
  365. // Reject 审批拒绝
  366. func (s *ctrContractAdvanceService) Reject(req *contractmodel.IdRequiredReq) error {
  367. entity, err := s.Dao.WherePri(req.Id).One()
  368. if err != nil {
  369. g.Log().Error(err)
  370. return myerrors.DbError("查询提前执行申请数据失败")
  371. }
  372. if entity == nil {
  373. return myerrors.TipsError("提前执行申请数据不存在")
  374. }
  375. // 校验状态:只有审核中(20)状态可以拒绝
  376. if entity.ApproStatus != contractmodel.AdvanceStatusApproving {
  377. return myerrors.TipsError("当前状态不可审批拒绝")
  378. }
  379. // 更新状态为审核拒绝
  380. _, err = s.Dao.WherePri(req.Id).Data(g.Map{
  381. "appro_status": contractmodel.AdvanceStatusRejected,
  382. }).Update()
  383. if err != nil {
  384. g.Log().Error(err)
  385. return myerrors.DbError("更新审批状态失败")
  386. }
  387. return nil
  388. }
  389. // ConvertToContract 转为正式合同
  390. func (s *ctrContractAdvanceService) ConvertToContract(req *contractmodel.CtrContractAdvanceConvertReq) error {
  391. entity, err := s.Dao.WherePri(req.Id).One()
  392. if err != nil {
  393. g.Log().Error(err)
  394. return myerrors.DbError("查询提前执行申请数据失败")
  395. }
  396. if entity == nil {
  397. return myerrors.TipsError("提前执行申请数据不存在")
  398. }
  399. // 校验状态:只有审核通过(30)状态可以转正式合同
  400. if entity.ApproStatus != contractmodel.AdvanceStatusApproved {
  401. return myerrors.TipsError("只有审核通过的申请才能转为正式合同")
  402. }
  403. // 校验是否已转合同
  404. if entity.ContractId != 0 {
  405. return myerrors.TipsError("该申请已转为正式合同")
  406. }
  407. return s.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  408. // 更新提前执行申请为已转合同状态
  409. _, err = s.Dao.TX(tx).WherePri(req.Id).Data(g.Map{
  410. "contract_id": req.ContractId,
  411. "contract_code": req.ContractCode,
  412. "convert_time": gtime.Now(),
  413. "convert_by": s.GetCxtUserId(),
  414. "convert_name": s.GetCxtUserName(),
  415. "appro_status": contractmodel.AdvanceStatusConverted,
  416. }).Update()
  417. if err != nil {
  418. g.Log().Error(err)
  419. return myerrors.DbError("更新提前执行申请状态失败")
  420. }
  421. // 更新交付项目关联的合同ID,并将申请ID记录到attribute1
  422. deliveryProjectDao := opsdevdao.NewOpsDeliveryProjectDao(s.Tenant)
  423. _, err = deliveryProjectDao.TX(tx).Where("contract_id = ?", req.ContractId).Data(g.Map{
  424. "contract_id": req.ContractId,
  425. "attribute1": strconv.Itoa(req.Id), // 记录申请ID到attribute1
  426. "updated_time": gtime.Now(),
  427. }).Update()
  428. if err != nil {
  429. g.Log().Error(err)
  430. return myerrors.DbError("更新交付项目合同关联失败")
  431. }
  432. return nil
  433. })
  434. }
  435. // generateAdvanceCode 生成申请编号 TQ + 年月日 + 4位序号
  436. func (s *ctrContractAdvanceService) generateAdvanceCode() string {
  437. dateStr := time.Now().Format("20060102")
  438. seq := s.getNextSequence(dateStr)
  439. return fmt.Sprintf("TQ%s%04d", dateStr, seq)
  440. }
  441. // getNextSequence 获取当日序号
  442. func (s *ctrContractAdvanceService) getNextSequence(dateStr string) int {
  443. count, err := s.Dao.WhereLike("advance_code", fmt.Sprintf("TQ%s%%", dateStr)).Count()
  444. if err != nil {
  445. g.Log().Error(err)
  446. return 1
  447. }
  448. return count + 1
  449. }
  450. // CreateDynamic 创建业务动态
  451. func (s *ctrContractAdvanceService) CreateDynamic(tenant string, businessId int, opnType, opnContent string, userId int, userName string) {
  452. dynamic := map[string]interface{}{
  453. "business_id": businessId,
  454. "business_type": "contract_advance",
  455. "opn_type": opnType,
  456. "opn_content": opnContent,
  457. "opn_people_id": userId,
  458. "opn_people": userName,
  459. "opn_date": gtime.Now(),
  460. "created_time": gtime.Now(),
  461. }
  462. _, err := s.Dao.DB.Model("business_dynamic").Data(dynamic).Insert()
  463. if err != nil {
  464. g.Log().Error("创建业务动态失败:", err)
  465. }
  466. }
  467. // AdvanceApplyApproval 审批回调处理
  468. func AdvanceApplyApproval(ctx context.Context, flow *workflowmodel.PlatWorkflow, msg *message.MixMessage) error {
  469. ctrContractAdvanceMtx.Lock()
  470. defer ctrContractAdvanceMtx.Unlock()
  471. tenant, err := micro_srv.GetTenant(ctx)
  472. if err != nil {
  473. return fmt.Errorf("获取租户码异常:%s", err.Error())
  474. }
  475. advanceDao := contractdao.NewCtrContractAdvanceDao(tenant)
  476. advanceId, err := strconv.Atoi(flow.BizCode)
  477. if err != nil {
  478. return fmt.Errorf("提前执行申请审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id)
  479. }
  480. advance, err := advanceDao.Where("id = ?", advanceId).One()
  481. if err != nil {
  482. return err
  483. }
  484. if advance == nil {
  485. return fmt.Errorf("提前执行申请不存在:%s Id: %d", flow.BizCode, flow.Id)
  486. }
  487. // 校验回调类型和结果
  488. if msg.ProcessType != "finish" && msg.ProcessType != "terminate" {
  489. return fmt.Errorf("无法识别的 ProcessType:%s", msg.ProcessType)
  490. }
  491. if msg.Result != "agree" && msg.Result != "refuse" && msg.Result != "" {
  492. return fmt.Errorf("无法识别的 Result:%s", msg.Result)
  493. }
  494. // 确定新状态
  495. var status string
  496. if msg.ProcessType == "terminate" {
  497. status = contractmodel.AdvanceStatusCancelled
  498. } else {
  499. if msg.Result == "agree" {
  500. status = contractmodel.AdvanceStatusApproved
  501. } else {
  502. status = contractmodel.AdvanceStatusRejected
  503. }
  504. }
  505. // 更新状态
  506. _, err = advanceDao.Where("id = ?", advanceId).Data(g.Map{
  507. "appro_status": status,
  508. }).Update()
  509. if err != nil {
  510. return err
  511. }
  512. // 如果审批通过,自动创建交付项目(参考合同审批通过逻辑)
  513. if status == contractmodel.AdvanceStatusApproved {
  514. err = advanceDao.DB.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
  515. // 创建交付项目,使用申请创建人作为项目创建人
  516. err := CreateProjectByAdvance(tx, tenant, advance, advance.CreatedBy, advance.CreatedName)
  517. if err != nil {
  518. return err
  519. }
  520. return nil
  521. })
  522. if err != nil {
  523. return err
  524. }
  525. }
  526. return nil
  527. }
  528. // CreateProjectByAdvance 根据提前执行申请创建交付项目
  529. func CreateProjectByAdvance(tx *gdb.TX, tenant string, advance *contractmodel.CtrContractAdvance, createdBy int, createdName string) error {
  530. if advance == nil {
  531. return fmt.Errorf("提前执行申请信息不能为空")
  532. }
  533. projectDao := opsdevdao.NewOpsDeliveryProjectDao(tenant)
  534. // 幂等创建:检查是否已存在
  535. count, err := projectDao.TX(tx).Where("attribute1 = ? AND deleted_time IS NULL", strconv.Itoa(advance.Id)).Count()
  536. if err != nil {
  537. return err
  538. }
  539. if count > 0 {
  540. return nil
  541. }
  542. // 创建交付项目
  543. data := g.Map{
  544. "project_name": advance.AdvanceName,
  545. "project_status": "10",
  546. "contract_id": advance.Id, // 申请ID作为临时合同ID
  547. "contract_no": advance.AdvanceCode, // 申请编号作为临时合同编号
  548. "cust_id": strconv.Itoa(advance.CustId),
  549. "cust_name": advance.CustName,
  550. "product_line": advance.ProductLine,
  551. "sales_user_id": advance.InchargeId,
  552. "sales_user_name": advance.InchargeName,
  553. "sales_region_id": advance.CustCityId,
  554. "delivery_node": "05",
  555. "attribute1": strconv.Itoa(advance.Id), // 记录提前执行申请ID
  556. "created_by": createdBy,
  557. "created_name": createdName,
  558. "created_time": gtime.Now(),
  559. "updated_time": gtime.Now(),
  560. }
  561. _, err = projectDao.TX(tx).Data(data).Insert()
  562. return err
  563. }
  564. // UpdateAdvanceContract 更新提前执行申请的正式合同信息
  565. func (s *ctrContractAdvanceService) UpdateAdvanceContract(req *contractmodel.CtrContractAdvanceConvertReq) error {
  566. if req.Id <= 0 {
  567. return myerrors.TipsError("提前执行申请ID不能为空")
  568. }
  569. if req.ContractId <= 0 {
  570. return myerrors.TipsError("合同ID不能为空")
  571. }
  572. if req.ContractCode == "" {
  573. return myerrors.TipsError("合同编号不能为空")
  574. }
  575. return s.Dao.DB.Transaction(s.Ctx, func(ctx context.Context, tx *gdb.TX) error {
  576. // 1. 更新提前执行申请表
  577. _, err := tx.Update("ctr_contract_advance", g.Map{
  578. "contract_id": req.ContractId,
  579. "contract_code": req.ContractCode,
  580. "appro_status": "60", // 已转正式合同
  581. }, "id = ?", req.Id)
  582. if err != nil {
  583. return err
  584. }
  585. // 2. 更新交付项目表
  586. projectDao := opsdevdao.NewOpsDeliveryProjectDao(s.Tenant)
  587. _, err = projectDao.TX(tx).Data(g.Map{
  588. "contract_id": req.ContractId,
  589. "contract_no": req.ContractCode,
  590. }).Where("attribute1 = ?", strconv.Itoa(req.Id)).Update()
  591. return err
  592. })
  593. }